Merge "Don't crash if the perf map creation failed"
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index d68835a..af6f91f 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -1076,7 +1076,7 @@
pc_rel_temp_(nullptr),
dex_cache_arrays_min_offset_(std::numeric_limits<uint32_t>::max()),
cfi_(&last_lir_insn_,
- cu->compiler_driver->GetCompilerOptions().GetGenerateDebugInfo(),
+ cu->compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo(),
arena),
in_to_reg_storage_mapping_(arena) {
switch_tables_.reserve(4);
diff --git a/compiler/dex/quick/quick_cfi_test.cc b/compiler/dex/quick/quick_cfi_test.cc
index c5df134..0cd41bb 100644
--- a/compiler/dex/quick/quick_cfi_test.cc
+++ b/compiler/dex/quick/quick_cfi_test.cc
@@ -71,6 +71,7 @@
nullptr,
false,
"",
+ false,
false);
VerificationResults verification_results(&compiler_options);
DexFileToMethodInlinerMap method_inliner_map;
diff --git a/compiler/dex/quick/x86/quick_assemble_x86_test.cc b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
index d63878d..efdc333 100644
--- a/compiler/dex/quick/x86/quick_assemble_x86_test.cc
+++ b/compiler/dex/quick/x86/quick_assemble_x86_test.cc
@@ -54,6 +54,7 @@
nullptr,
false,
"",
+ false,
false));
verification_results_.reset(new VerificationResults(compiler_options_.get()));
method_inliner_map_.reset(new DexFileToMethodInlinerMap());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 818d50a..f1b7458 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -39,6 +39,7 @@
#include "compiler_driver-inl.h"
#include "dex_compilation_unit.h"
#include "dex_file-inl.h"
+#include "dex_instruction-inl.h"
#include "dex/dex_to_dex_compiler.h"
#include "dex/verification_results.h"
#include "dex/verified_method.h"
@@ -365,7 +366,7 @@
classes_to_compile_(compiled_classes),
methods_to_compile_(compiled_methods),
had_hard_verifier_failure_(false),
- thread_count_(thread_count),
+ parallel_thread_count_(thread_count),
stats_(new AOTCompilationStats),
dump_stats_(dump_stats),
dump_passes_(dump_passes),
@@ -435,24 +436,27 @@
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
- std::unique_ptr<ThreadPool> thread_pool(
- new ThreadPool("Compiler driver thread pool", thread_count_ - 1));
+
+ InitializeThreadPools();
+
VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
// Precompile:
// 1) Load image classes
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
- PreCompile(class_loader, dex_files, thread_pool.get(), timings);
+ PreCompile(class_loader, dex_files, timings);
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
// compilation.
if (!GetCompilerOptions().VerifyAtRuntime()) {
- Compile(class_loader, dex_files, thread_pool.get(), timings);
+ Compile(class_loader, dex_files, timings);
}
if (dump_stats_) {
stats_->Dump();
}
+
+ FreeThreadPools();
}
static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel(
@@ -653,8 +657,9 @@
std::vector<const DexFile*> dex_files;
dex_files.push_back(dex_file);
- std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", 0U));
- PreCompile(jclass_loader, dex_files, thread_pool.get(), timings);
+ InitializeThreadPools();
+
+ PreCompile(jclass_loader, dex_files, timings);
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -677,20 +682,147 @@
true,
dex_cache);
+ FreeThreadPools();
+
self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
}
-void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Resolve(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // Resolution allocates classes and needs to run single-threaded to be deterministic.
+ bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+ ThreadPool* resolve_thread_pool = force_determinism
+ ? single_thread_pool_.get()
+ : parallel_thread_pool_.get();
+ size_t resolve_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- ResolveDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ ResolveDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ resolve_thread_pool,
+ resolve_thread_count,
+ timings);
}
}
-void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now
+// this is single-threaded for simplicity.
+// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a
+// stable order.
+
+static void ResolveConstStrings(CompilerDriver* driver,
+ const DexFile& dex_file,
+ const DexFile::CodeItem* code_item) {
+ if (code_item == nullptr) {
+ // Abstract or native method.
+ return;
+ }
+
+ const uint16_t* code_ptr = code_item->insns_;
+ const uint16_t* code_end = code_item->insns_ + code_item->insns_size_in_code_units_;
+
+ while (code_ptr < code_end) {
+ const Instruction* inst = Instruction::At(code_ptr);
+ switch (inst->Opcode()) {
+ case Instruction::CONST_STRING: {
+ uint32_t string_index = inst->VRegB_21c();
+ driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+ break;
+ }
+ case Instruction::CONST_STRING_JUMBO: {
+ uint32_t string_index = inst->VRegB_31c();
+ driver->CanAssumeStringIsPresentInDexCache(dex_file, string_index);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ code_ptr += inst->SizeInCodeUnits();
+ }
+}
+
+static void ResolveConstStrings(CompilerDriver* driver,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ for (const DexFile* dex_file : dex_files) {
+ TimingLogger::ScopedTiming t("Resolve const-string Strings", timings);
+
+ size_t class_def_count = dex_file->NumClassDefs();
+ for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
+ const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+
+ const uint8_t* class_data = dex_file->GetClassData(class_def);
+ if (class_data == nullptr) {
+ // empty class, probably a marker interface
+ continue;
+ }
+
+ ClassDataItemIterator it(*dex_file, class_data);
+ // Skip fields
+ while (it.HasNextStaticField()) {
+ it.Next();
+ }
+ while (it.HasNextInstanceField()) {
+ it.Next();
+ }
+
+ bool compilation_enabled = driver->IsClassToCompile(
+ dex_file->StringByTypeIdx(class_def.class_idx_));
+ if (!compilation_enabled) {
+ // Compilation is skipped, do not resolve const-string in code of this class.
+ // TODO: Make sure that inlining honors this.
+ continue;
+ }
+
+ // Direct methods.
+ int64_t previous_direct_method_idx = -1;
+ while (it.HasNextDirectMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_direct_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_direct_method_idx = method_idx;
+ ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+ it.Next();
+ }
+ // Virtual methods.
+ int64_t previous_virtual_method_idx = -1;
+ while (it.HasNextVirtualMethod()) {
+ uint32_t method_idx = it.GetMemberIndex();
+ if (method_idx == previous_virtual_method_idx) {
+ // smali can create dex files with two encoded_methods sharing the same method_idx
+ // http://code.google.com/p/smali/issues/detail?id=119
+ it.Next();
+ continue;
+ }
+ previous_virtual_method_idx = method_idx;
+ ResolveConstStrings(driver, *dex_file, it.GetMethodCodeItem());
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
+ }
+ }
+}
+
+inline void CompilerDriver::CheckThreadPools() {
+ DCHECK(parallel_thread_pool_ != nullptr);
+ DCHECK(single_thread_pool_ != nullptr);
+}
+
+void CompilerDriver::PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ CheckThreadPools();
+
LoadImageClasses(timings);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
@@ -700,20 +832,26 @@
// We need to resolve for never_verify since it needs to run dex to dex to add the
// RETURN_VOID_NO_BARRIER.
if (never_verify || verification_enabled) {
- Resolve(class_loader, dex_files, thread_pool, timings);
+ Resolve(class_loader, dex_files, timings);
VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false);
}
if (never_verify) {
VLOG(compiler) << "Verify none mode specified, skipping verification.";
- SetVerified(class_loader, dex_files, thread_pool, timings);
+ SetVerified(class_loader, dex_files, timings);
}
if (!verification_enabled) {
return;
}
- Verify(class_loader, dex_files, thread_pool, timings);
+ if (GetCompilerOptions().IsForceDeterminism() && IsBootImage()) {
+ // Resolve strings from const-string. Do this now to have a deterministic image.
+ ResolveConstStrings(this, dex_files, timings);
+ VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
+ }
+
+ Verify(class_loader, dex_files, timings);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -721,7 +859,7 @@
<< "situations. Please check the log.";
}
- InitializeClasses(class_loader, dex_files, thread_pool, timings);
+ InitializeClasses(class_loader, dex_files, timings);
VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
UpdateImageClasses(timings);
@@ -1759,6 +1897,9 @@
// Wait for all the worker threads to finish.
thread_pool_->Wait(self, true, false);
+
+ // And stop the workers accepting jobs.
+ thread_pool_->StopWorkers(self);
}
size_t NextIndex() {
@@ -1995,9 +2136,12 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::ResolveDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// TODO: we could resolve strings here, although the string table is largely filled with class
@@ -2010,27 +2154,43 @@
// classdefs are resolved by ResolveClassFieldsAndMethods.
TimingLogger::ScopedTiming t("Resolve Types", timings);
ResolveTypeVisitor visitor(&context);
- context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumTypeIds(), &visitor, thread_count);
}
TimingLogger::ScopedTiming t("Resolve MethodsAndFields", timings);
ResolveClassFieldsAndMethodsVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
-void CompilerDriver::SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::SetVerified(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // This can be run in parallel.
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
- SetVerifiedDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ SetVerifiedDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
}
-void CompilerDriver::Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Verify(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
+ // Note: verification should not be pulling in classes anymore when compiling the boot image,
+ // as all should have been resolved before. As such, doing this in parallel should still
+ // be deterministic.
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
- VerifyDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ VerifyDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
}
@@ -2104,15 +2264,18 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::VerifyDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Verify Dex File", timings);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
VerifyClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
class SetVerifiedClassVisitor : public CompilationVisitor {
@@ -2162,15 +2325,18 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::SetVerifiedDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Verify Dex File", timings);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, dex_files,
thread_pool);
SetVerifiedClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
class InitializeClassVisitor : public CompilationVisitor {
@@ -2271,31 +2437,37 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file,
+void CompilerDriver::InitializeClasses(jobject jni_class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("InitializeNoClinit", timings);
+
+ // Initialization allocates objects and needs to run single-threaded to be deterministic.
+ bool force_determinism = GetCompilerOptions().IsForceDeterminism();
+ ThreadPool* init_thread_pool = force_determinism
+ ? single_thread_pool_.get()
+ : parallel_thread_pool_.get();
+ size_t init_thread_count = force_determinism ? 1U : parallel_thread_count_;
+
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, dex_files,
- thread_pool);
- size_t thread_count;
+ init_thread_pool);
if (IsBootImage()) {
// TODO: remove this when transactional mode supports multithreading.
- thread_count = 1U;
- } else {
- thread_count = thread_count_;
+ init_thread_count = 1U;
}
InitializeClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, init_thread_count);
}
void CompilerDriver::InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ TimingLogger* timings) {
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- InitializeClasses(class_loader, *dex_file, dex_files, thread_pool, timings);
+ InitializeClasses(class_loader, *dex_file, dex_files, timings);
}
if (IsBootImage()) {
// Prune garbage objects created during aborted transactions.
@@ -2303,8 +2475,9 @@
}
}
-void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+void CompilerDriver::Compile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings) {
if (kDebugProfileGuidedCompilation) {
LOG(INFO) << "[ProfileGuidedCompilation] " <<
((profile_compilation_info_ == nullptr)
@@ -2314,7 +2487,12 @@
for (size_t i = 0; i != dex_files.size(); ++i) {
const DexFile* dex_file = dex_files[i];
CHECK(dex_file != nullptr);
- CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
+ CompileDexFile(class_loader,
+ *dex_file,
+ dex_files,
+ parallel_thread_pool_.get(),
+ parallel_thread_count_,
+ timings);
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}
@@ -2421,14 +2599,17 @@
const ParallelCompilationManager* const manager_;
};
-void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
+void CompilerDriver::CompileDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings) {
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings) {
TimingLogger::ScopedTiming t("Compile Dex File", timings);
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
&dex_file, dex_files, thread_pool);
CompileClassVisitor visitor(&context);
- context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count_);
+ context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}
void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref,
@@ -2582,11 +2763,24 @@
const DexFile* inlined_into) const {
// We're not allowed to inline across dex files if we're the no-inline-from dex file.
if (inlined_from != inlined_into &&
- compiler_options_->GetNoInlineFromDexFile() == inlined_from) {
+ compiler_options_->GetNoInlineFromDexFile() != nullptr &&
+ ContainsElement(*compiler_options_->GetNoInlineFromDexFile(), inlined_from)) {
return false;
}
return true;
}
+void CompilerDriver::InitializeThreadPools() {
+ size_t parallel_count = parallel_thread_count_ > 0 ? parallel_thread_count_ - 1 : 0;
+ parallel_thread_pool_.reset(
+ new ThreadPool("Compiler driver thread pool", parallel_count));
+ single_thread_pool_.reset(new ThreadPool("Single-threaded Compiler driver thread pool", 0));
+}
+
+void CompilerDriver::FreeThreadPools() {
+ parallel_thread_pool_.reset();
+ single_thread_pool_.reset();
+}
+
} // namespace art
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 6a2f7bf..5e35cbb 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -411,7 +411,7 @@
}
size_t GetThreadCount() const {
- return thread_count_;
+ return parallel_thread_count_;
}
bool GetDumpStats() const {
@@ -550,8 +550,9 @@
SHARED_REQUIRES(Locks::mutator_lock_);
private:
- void PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ void PreCompile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
@@ -559,49 +560,71 @@
// Attempt to resolve all type, methods, fields, and strings
// referenced from code in the dex file following PathClassLoader
// ordering semantics.
- void Resolve(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ void Resolve(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void ResolveDexFile(jobject class_loader, const DexFile& dex_file,
+ void ResolveDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void Verify(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void VerifyDexFile(jobject class_loader, const DexFile& dex_file,
+ void Verify(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void VerifyDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void SetVerified(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file,
+ void SetVerified(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void SetVerifiedDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
- void InitializeClasses(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
- REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
- void InitializeClasses(jobject class_loader, const DexFile& dex_file,
+ void InitializeClasses(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ TimingLogger* timings)
+ REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
+ void InitializeClasses(jobject class_loader,
+ const DexFile& dex_file,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
void UpdateImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
static void FindClinitImageClassesCallback(mirror::Object* object, void* arg)
SHARED_REQUIRES(Locks::mutator_lock_);
- void Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings);
- void CompileDexFile(jobject class_loader, const DexFile& dex_file,
+ void Compile(jobject class_loader,
+ const std::vector<const DexFile*>& dex_files,
+ TimingLogger* timings);
+ void CompileDexFile(jobject class_loader,
+ const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
- ThreadPool* thread_pool, TimingLogger* timings)
+ ThreadPool* thread_pool,
+ size_t thread_count,
+ TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
bool MayInlineInternal(const DexFile* inlined_from, const DexFile* inlined_into) const;
+ void InitializeThreadPools();
+ void FreeThreadPools();
+ void CheckThreadPools();
+
const CompilerOptions* const compiler_options_;
VerificationResults* const verification_results_;
DexFileToMethodInlinerMap* const method_inliner_map_;
@@ -652,7 +675,12 @@
bool had_hard_verifier_failure_;
- size_t thread_count_;
+ // A thread pool that can (potentially) run tasks in parallel.
+ std::unique_ptr<ThreadPool> parallel_thread_pool_;
+ size_t parallel_thread_count_;
+
+ // A thread pool that guarantees running single-threaded on the main thread.
+ std::unique_ptr<ThreadPool> single_thread_pool_;
class AOTCompilationStats;
std::unique_ptr<AOTCompilationStats> stats_;
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 2644528..3bf8921 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -37,6 +37,7 @@
debuggable_(false),
native_debuggable_(kDefaultNativeDebuggable),
generate_debug_info_(kDefaultGenerateDebugInfo),
+ generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
implicit_null_checks_(true),
implicit_so_checks_(true),
implicit_suspend_checks_(false),
@@ -46,7 +47,8 @@
abort_on_hard_verifier_failure_(false),
init_failure_output_(nullptr),
dump_cfg_file_name_(""),
- dump_cfg_append_(false) {
+ dump_cfg_append_(false),
+ force_determinism_(false) {
}
CompilerOptions::~CompilerOptions() {
@@ -62,7 +64,7 @@
size_t num_dex_methods_threshold,
size_t inline_depth_limit,
size_t inline_max_code_units,
- const DexFile* no_inline_from,
+ const std::vector<const DexFile*>* no_inline_from,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -75,7 +77,8 @@
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure,
const std::string& dump_cfg_file_name,
- bool dump_cfg_append
+ bool dump_cfg_append,
+ bool force_determinism
) : // NOLINT(whitespace/parens)
compiler_filter_(compiler_filter),
huge_method_threshold_(huge_method_threshold),
@@ -91,6 +94,7 @@
debuggable_(debuggable),
native_debuggable_(kDefaultNativeDebuggable),
generate_debug_info_(generate_debug_info),
+ generate_mini_debug_info_(kDefaultGenerateMiniDebugInfo),
implicit_null_checks_(implicit_null_checks),
implicit_so_checks_(implicit_so_checks),
implicit_suspend_checks_(implicit_suspend_checks),
@@ -100,7 +104,8 @@
abort_on_hard_verifier_failure_(abort_on_hard_verifier_failure),
init_failure_output_(init_failure_output),
dump_cfg_file_name_(dump_cfg_file_name),
- dump_cfg_append_(dump_cfg_append) {
+ dump_cfg_append_(dump_cfg_append),
+ force_determinism_(force_determinism) {
}
void CompilerOptions::ParseHugeMethodMax(const StringPiece& option, UsageFn Usage) {
@@ -215,6 +220,10 @@
generate_debug_info_ = true;
} else if (option == "--no-generate-debug-info") {
generate_debug_info_ = false;
+ } else if (option == "--generate-mini-debug-info") {
+ generate_mini_debug_info_ = true;
+ } else if (option == "--no-generate-mini-debug-info") {
+ generate_mini_debug_info_ = false;
} else if (option == "--debuggable") {
debuggable_ = true;
} else if (option == "--native-debuggable") {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index d47fc2a..39372b3 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -51,6 +51,7 @@
static constexpr double kDefaultTopKProfileThreshold = 90.0;
static const bool kDefaultNativeDebuggable = false;
static const bool kDefaultGenerateDebugInfo = false;
+ static const bool kDefaultGenerateMiniDebugInfo = false;
static const bool kDefaultIncludePatchInformation = false;
static const size_t kDefaultInlineDepthLimit = 3;
static const size_t kDefaultInlineMaxCodeUnits = 32;
@@ -72,7 +73,7 @@
size_t num_dex_methods_threshold,
size_t inline_depth_limit,
size_t inline_max_code_units,
- const DexFile* no_inline_from,
+ const std::vector<const DexFile*>* no_inline_from,
bool include_patch_information,
double top_k_profile_threshold,
bool debuggable,
@@ -85,7 +86,8 @@
std::ostream* init_failure_output,
bool abort_on_hard_verifier_failure,
const std::string& dump_cfg_file_name,
- bool dump_cfg_append);
+ bool dump_cfg_append,
+ bool force_determinism);
CompilerFilter GetCompilerFilter() const {
return compiler_filter_;
@@ -170,10 +172,20 @@
return native_debuggable_;
}
+ // This flag controls whether the compiler collects debugging information.
+ // The other flags control how the information is written to disk.
+ bool GenerateAnyDebugInfo() const {
+ return GetGenerateDebugInfo() || GetGenerateMiniDebugInfo();
+ }
+
bool GetGenerateDebugInfo() const {
return generate_debug_info_;
}
+ bool GetGenerateMiniDebugInfo() const {
+ return generate_mini_debug_info_;
+ }
+
bool GetImplicitNullChecks() const {
return implicit_null_checks_;
}
@@ -220,7 +232,7 @@
return abort_on_hard_verifier_failure_;
}
- const DexFile* GetNoInlineFromDexFile() const {
+ const std::vector<const DexFile*>* GetNoInlineFromDexFile() const {
return no_inline_from_;
}
@@ -234,6 +246,10 @@
return dump_cfg_append_;
}
+ bool IsForceDeterminism() const {
+ return force_determinism_;
+ }
+
private:
void ParseDumpInitFailures(const StringPiece& option, UsageFn Usage);
void ParsePassOptions(const StringPiece& option, UsageFn Usage);
@@ -257,8 +273,10 @@
size_t inline_depth_limit_;
size_t inline_max_code_units_;
- // A dex file from which we should not inline code.
- const DexFile* no_inline_from_;
+ // Dex files from which we should not inline code.
+ // This is usually a very short list (i.e. a single dex file), so we
+ // prefer vector<> over a lookup-oriented container, such as set<>.
+ const std::vector<const DexFile*>* no_inline_from_;
bool include_patch_information_;
// When using a profile file only the top K% of the profiled samples will be compiled.
@@ -266,6 +284,7 @@
bool debuggable_;
bool native_debuggable_;
bool generate_debug_info_;
+ bool generate_mini_debug_info_;
bool implicit_null_checks_;
bool implicit_so_checks_;
bool implicit_suspend_checks_;
@@ -286,6 +305,10 @@
std::string dump_cfg_file_name_;
bool dump_cfg_append_;
+ // Whether the compiler should trade performance for determinism to guarantee exactly reproducable
+ // outcomes.
+ bool force_determinism_;
+
friend class Dex2Oat;
DISALLOW_COPY_AND_ASSIGN(CompilerOptions);
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 46484b1..3d24d19 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -165,10 +165,15 @@
}
}
- // Set desired allocation size for .bss section.
- void SetSize(Elf_Word size) {
- CHECK_EQ(header_.sh_type, (Elf_Word)SHT_NOBITS);
+ // Write this section as "NOBITS" section. (used for the .bss section)
+ // This means that the ELF file does not contain the initial data for this section
+ // and it will be zero-initialized when the ELF file is loaded in the running program.
+ void WriteNoBitsSection(Elf_Word size) {
+ DCHECK_NE(header_.sh_flags & SHF_ALLOC, 0u);
+ Start();
+ header_.sh_type = SHT_NOBITS;
header_.sh_size = size;
+ End();
}
// This function always succeeds to simplify code.
@@ -346,6 +351,12 @@
other_sections_.push_back(std::move(s));
}
+ // Set where the next section will be allocated in the virtual address space.
+ void SetVirtualAddress(Elf_Addr address) {
+ DCHECK_GE(address, virtual_address_);
+ virtual_address_ = address;
+ }
+
void Start() {
// Reserve space for ELF header and program headers.
// We do not know the number of headers until later, so
diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc
index 73e6aa3..2e98b69 100644
--- a/compiler/elf_writer_debug.cc
+++ b/compiler/elf_writer_debug.cc
@@ -16,6 +16,7 @@
#include "elf_writer_debug.h"
+#include <algorithm>
#include <unordered_set>
#include <vector>
#include <cstdio>
@@ -40,6 +41,11 @@
#include "stack_map.h"
#include "utils.h"
+// liblzma.
+#include "XzEnc.h"
+#include "7zCrc.h"
+#include "XzCrc64.h"
+
namespace art {
namespace dwarf {
@@ -222,7 +228,8 @@
template<typename ElfTypes>
void WriteCFISection(ElfBuilder<ElfTypes>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
- CFIFormat format) {
+ CFIFormat format,
+ bool write_oat_patches) {
CHECK(format == DW_DEBUG_FRAME_FORMAT || format == DW_EH_FRAME_FORMAT);
typedef typename ElfTypes::Addr Elf_Addr;
@@ -238,6 +245,24 @@
patch_locations.reserve(method_infos.size());
}
+ // The methods can be written any order.
+ // Let's therefore sort them in the lexicographical order of the opcodes.
+ // This has no effect on its own. However, if the final .debug_frame section is
+ // compressed it reduces the size since similar opcodes sequences are grouped.
+ std::vector<const MethodDebugInfo*> sorted_method_infos;
+ sorted_method_infos.reserve(method_infos.size());
+ for (size_t i = 0; i < method_infos.size(); i++) {
+ sorted_method_infos.push_back(&method_infos[i]);
+ }
+ std::sort(
+ sorted_method_infos.begin(),
+ sorted_method_infos.end(),
+ [](const MethodDebugInfo* lhs, const MethodDebugInfo* rhs) {
+ ArrayRef<const uint8_t> l = lhs->compiled_method_->GetCFIInfo();
+ ArrayRef<const uint8_t> r = rhs->compiled_method_->GetCFIInfo();
+ return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());
+ });
+
// Write .eh_frame/.debug_frame section.
auto* cfi_section = (format == DW_DEBUG_FRAME_FORMAT
? builder->GetDebugFrame()
@@ -256,11 +281,11 @@
cfi_section->WriteFully(buffer.data(), buffer.size());
buffer_address += buffer.size();
buffer.clear();
- for (const MethodDebugInfo& mi : method_infos) {
- if (!mi.deduped_) { // Only one FDE per unique address.
- ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo();
+ for (const MethodDebugInfo* mi : sorted_method_infos) {
+ if (!mi->deduped_) { // Only one FDE per unique address.
+ ArrayRef<const uint8_t> opcodes = mi->compiled_method_->GetCFIInfo();
if (!opcodes.empty()) {
- const Elf_Addr code_address = text_address + mi.low_pc_;
+ const Elf_Addr code_address = text_address + mi->low_pc_;
if (format == DW_EH_FRAME_FORMAT) {
binary_search_table.push_back(
dchecked_integral_cast<uint32_t>(code_address));
@@ -268,7 +293,7 @@
dchecked_integral_cast<uint32_t>(buffer_address));
}
WriteFDE(is64bit, cfi_address, cie_address,
- code_address, mi.high_pc_ - mi.low_pc_,
+ code_address, mi->high_pc_ - mi->low_pc_,
opcodes, format, buffer_address, &buffer,
&patch_locations);
cfi_section->WriteFully(buffer.data(), buffer.size());
@@ -309,8 +334,10 @@
header_section->WriteFully(binary_search_table.data(), binary_search_table.size());
header_section->End();
} else {
- builder->WritePatches(".debug_frame.oat_patches",
- ArrayRef<const uintptr_t>(patch_locations));
+ if (write_oat_patches) {
+ builder->WritePatches(".debug_frame.oat_patches",
+ ArrayRef<const uintptr_t>(patch_locations));
+ }
}
}
@@ -1338,8 +1365,9 @@
}
template <typename ElfTypes>
-void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
- const ArrayRef<const MethodDebugInfo>& method_infos) {
+static void WriteDebugSymbols(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos,
+ bool with_signature) {
bool generated_mapping_symbol = false;
auto* strtab = builder->GetStrTab();
auto* symtab = builder->GetSymTab();
@@ -1359,22 +1387,31 @@
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
+ std::string last_name;
+ size_t last_name_offset = 0;
for (const MethodDebugInfo& info : method_infos) {
if (info.deduped_) {
continue; // Add symbol only for the first instance.
}
- std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, true);
+ std::string name = PrettyMethod(info.dex_method_index_, *info.dex_file_, with_signature);
if (deduped_addresses.find(info.low_pc_) != deduped_addresses.end()) {
name += " [DEDUPED]";
}
+ // If we write method names without signature, we might see the same name multiple times.
+ size_t name_offset = (name == last_name ? last_name_offset : strtab->Write(name));
const auto* text = builder->GetText()->Exists() ? builder->GetText() : nullptr;
const bool is_relative = (text != nullptr);
uint32_t low_pc = info.low_pc_;
// Add in code delta, e.g., thumb bit 0 for Thumb2 code.
low_pc += info.compiled_method_->CodeDelta();
- symtab->Add(strtab->Write(name), text, low_pc,
- is_relative, info.high_pc_ - info.low_pc_, STB_GLOBAL, STT_FUNC);
+ symtab->Add(name_offset,
+ text,
+ low_pc,
+ is_relative,
+ info.high_pc_ - info.low_pc_,
+ STB_GLOBAL,
+ STT_FUNC);
// Conforming to aaelf, add $t mapping symbol to indicate start of a sequence of thumb2
// instructions, so that disassembler tools can correctly disassemble.
@@ -1387,6 +1424,9 @@
generated_mapping_symbol = true;
}
}
+
+ last_name = std::move(name);
+ last_name_offset = name_offset;
}
strtab->End();
@@ -1402,13 +1442,83 @@
const ArrayRef<const MethodDebugInfo>& method_infos,
CFIFormat cfi_format) {
// Add methods to .symtab.
- WriteDebugSymbols(builder, method_infos);
+ WriteDebugSymbols(builder, method_infos, true /* with_signature */);
// Generate CFI (stack unwinding information).
- WriteCFISection(builder, method_infos, cfi_format);
+ WriteCFISection(builder, method_infos, cfi_format, true /* write_oat_patches */);
// Write DWARF .debug_* sections.
WriteDebugSections(builder, method_infos);
}
+static void XzCompress(const std::vector<uint8_t>* src, std::vector<uint8_t>* dst) {
+ // Configure the compression library.
+ CrcGenerateTable();
+ Crc64GenerateTable();
+ CLzma2EncProps lzma2Props;
+ Lzma2EncProps_Init(&lzma2Props);
+ lzma2Props.lzmaProps.level = 1; // Fast compression.
+ Lzma2EncProps_Normalize(&lzma2Props);
+ CXzProps props;
+ XzProps_Init(&props);
+ props.lzma2Props = &lzma2Props;
+ // Implement the required interface for communication (written in C so no virtual methods).
+ struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress {
+ static SRes ReadImpl(void* p, void* buf, size_t* size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqInStream*>(p));
+ *size = std::min(*size, ctx->src_->size() - ctx->src_pos_);
+ memcpy(buf, ctx->src_->data() + ctx->src_pos_, *size);
+ ctx->src_pos_ += *size;
+ return SZ_OK;
+ }
+ static size_t WriteImpl(void* p, const void* buf, size_t size) {
+ auto* ctx = static_cast<XzCallbacks*>(reinterpret_cast<ISeqOutStream*>(p));
+ const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf);
+ ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size);
+ return size;
+ }
+ static SRes ProgressImpl(void* , UInt64, UInt64) {
+ return SZ_OK;
+ }
+ size_t src_pos_;
+ const std::vector<uint8_t>* src_;
+ std::vector<uint8_t>* dst_;
+ };
+ XzCallbacks callbacks;
+ callbacks.Read = XzCallbacks::ReadImpl;
+ callbacks.Write = XzCallbacks::WriteImpl;
+ callbacks.Progress = XzCallbacks::ProgressImpl;
+ callbacks.src_pos_ = 0;
+ callbacks.src_ = src;
+ callbacks.dst_ = dst;
+ // Compress.
+ SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks);
+ CHECK_EQ(res, SZ_OK);
+}
+
+template <typename ElfTypes>
+void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* parent_builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos) {
+ const InstructionSet isa = parent_builder->GetIsa();
+ std::vector<uint8_t> buffer;
+ buffer.reserve(KB);
+ VectorOutputStream out("Mini-debug-info ELF file", &buffer);
+ std::unique_ptr<ElfBuilder<ElfTypes>> builder(new ElfBuilder<ElfTypes>(isa, &out));
+ builder->Start();
+ // Write .rodata and .text as NOBITS sections.
+ // This allows tools to detect virtual address relocation of the parent ELF file.
+ builder->SetVirtualAddress(parent_builder->GetRoData()->GetAddress());
+ builder->GetRoData()->WriteNoBitsSection(parent_builder->GetRoData()->GetSize());
+ builder->SetVirtualAddress(parent_builder->GetText()->GetAddress());
+ builder->GetText()->WriteNoBitsSection(parent_builder->GetText()->GetSize());
+ WriteDebugSymbols(builder.get(), method_infos, false /* with_signature */);
+ WriteCFISection(builder.get(), method_infos, DW_DEBUG_FRAME_FORMAT, false /* write_oat_paches */);
+ builder->End();
+ CHECK(builder->Good());
+ std::vector<uint8_t> compressed_buffer;
+ compressed_buffer.reserve(buffer.size() / 4);
+ XzCompress(&buffer, &compressed_buffer);
+ parent_builder->WriteSection(".gnu_debugdata", &compressed_buffer);
+}
+
template <typename ElfTypes>
static ArrayRef<const uint8_t> WriteDebugElfFileForMethodInternal(
const dwarf::MethodDebugInfo& method_info) {
@@ -1481,6 +1591,12 @@
ElfBuilder<ElfTypes64>* builder,
const ArrayRef<const MethodDebugInfo>& method_infos,
CFIFormat cfi_format);
+template void WriteMiniDebugInfo<ElfTypes32>(
+ ElfBuilder<ElfTypes32>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos);
+template void WriteMiniDebugInfo<ElfTypes64>(
+ ElfBuilder<ElfTypes64>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos);
} // namespace dwarf
} // namespace art
diff --git a/compiler/elf_writer_debug.h b/compiler/elf_writer_debug.h
index e4bc856..e19da08 100644
--- a/compiler/elf_writer_debug.h
+++ b/compiler/elf_writer_debug.h
@@ -35,6 +35,10 @@
const ArrayRef<const MethodDebugInfo>& method_infos,
CFIFormat cfi_format);
+template <typename ElfTypes>
+void WriteMiniDebugInfo(ElfBuilder<ElfTypes>* builder,
+ const ArrayRef<const MethodDebugInfo>& method_infos);
+
ArrayRef<const uint8_t> WriteDebugElfFileForMethod(const dwarf::MethodDebugInfo& method_info);
ArrayRef<const uint8_t> WriteDebugElfFileForClasses(const InstructionSet isa,
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index 7b1bdd7..6bf080a 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -137,9 +137,7 @@
void ElfWriterQuick<ElfTypes>::SetBssSize(size_t bss_size) {
auto* bss = builder_->GetBss();
if (bss_size != 0u) {
- bss->Start();
- bss->SetSize(bss_size);
- bss->End();
+ bss->WriteNoBitsSection(bss_size);
}
}
@@ -152,8 +150,13 @@
void ElfWriterQuick<ElfTypes>::WriteDebugInfo(
const ArrayRef<const dwarf::MethodDebugInfo>& method_infos) {
if (compiler_options_->GetGenerateDebugInfo()) {
+ // Generate all the debug information we can.
dwarf::WriteDebugInfo(builder_.get(), method_infos, kCFIFormat);
}
+ if (compiler_options_->GetGenerateMiniDebugInfo()) {
+ // Generate only some information and compress it.
+ dwarf::WriteMiniDebugInfo(builder_.get(), method_infos);
+ }
}
template <typename ElfTypes>
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index b65fb36..a5a7796 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -119,6 +119,7 @@
compiler_driver_->GetInstructionSet(),
compiler_driver_->GetInstructionSetFeatures(),
&key_value_store,
+ /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify.
&opened_dex_files_map,
&opened_dex_files);
ASSERT_TRUE(dex_files_ok);
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 72c615e..c8720ea 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1866,6 +1866,9 @@
orig->FixupNativePointers(copy, target_ptr_size_, NativeLocationVisitor(this, oat_filename));
FixupClassVisitor visitor(this, copy);
static_cast<mirror::Object*>(orig)->VisitReferences(visitor, visitor);
+
+ // Remove the clinitThreadId. This is required for image determinism.
+ copy->SetClinitThreadId(static_cast<pid_t>(0));
}
void ImageWriter::FixupObject(Object* orig, Object* copy) {
@@ -1993,6 +1996,10 @@
mirror::DexCache::SetElementPtrSize(copy_fields, i, copy, target_ptr_size_);
}
}
+
+ // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving
+ // compiler pointers in here will make the output non-deterministic.
+ copy_dex_cache->SetDexFile(nullptr);
}
const uint8_t* ImageWriter::GetOatAddress(OatAddress type) const {
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 083ba8f..6774758 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -112,7 +112,8 @@
/* init_failure_output */ nullptr,
/* abort_on_hard_verifier_failure */ false,
/* dump_cfg_file_name */ "",
- /* dump_cfg_append */ false));
+ /* dump_cfg_append */ false,
+ /* force_determinism */ false));
for (const std::string& argument : Runtime::Current()->GetCompilerOptions()) {
compiler_options_->ParseCompilerOption(argument, Usage);
}
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 52a2382..e920460 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -95,7 +95,7 @@
// Assembler that holds generated instructions
std::unique_ptr<Assembler> jni_asm(Assembler::Create(instruction_set, instruction_set_features));
- jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GetGenerateDebugInfo());
+ jni_asm->cfi().SetEnabled(driver->GetCompilerOptions().GenerateAnyDebugInfo());
// Offsets into data structures
// TODO: if cross compiling these offsets are for the host not the target
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index c0d15f3..cff2f47 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -126,7 +126,8 @@
bool WriteElf(File* file,
const std::vector<const DexFile*>& dex_files,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
for (const DexFile* dex_file : dex_files) {
@@ -139,12 +140,13 @@
return false;
}
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool WriteElf(File* file,
const std::vector<const char*>& dex_filenames,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
for (const char* dex_filename : dex_filenames) {
@@ -152,24 +154,26 @@
return false;
}
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool WriteElf(File* file,
ScopedFd&& zip_fd,
const char* location,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
TimingLogger timings("WriteElf", false, false);
OatWriter oat_writer(/*compiling_boot_image*/false, &timings);
if (!oat_writer.AddZippedDexFilesSource(std::move(zip_fd), location)) {
return false;
}
- return DoWriteElf(file, oat_writer, key_value_store);
+ return DoWriteElf(file, oat_writer, key_value_store, verify);
}
bool DoWriteElf(File* file,
OatWriter& oat_writer,
- SafeMap<std::string, std::string>& key_value_store) {
+ SafeMap<std::string, std::string>& key_value_store,
+ bool verify) {
std::unique_ptr<ElfWriter> elf_writer = CreateElfWriterQuick(
compiler_driver_->GetInstructionSet(),
&compiler_driver_->GetCompilerOptions(),
@@ -183,6 +187,7 @@
compiler_driver_->GetInstructionSet(),
compiler_driver_->GetInstructionSetFeatures(),
&key_value_store,
+ verify,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
@@ -219,6 +224,9 @@
return elf_writer->End();
}
+ void TestDexFileInput(bool verify);
+ void TestZipFileInput(bool verify);
+
std::unique_ptr<const InstructionSetFeatures> insn_features_;
std::unique_ptr<QuickCompilerCallbacks> callbacks_;
};
@@ -354,7 +362,7 @@
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "lue.art");
- bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store);
+ bool success = WriteElf(tmp.GetFile(), class_linker->GetBootClassPath(), key_value_store, false);
ASSERT_TRUE(success);
if (kCompile) { // OatWriter strips the code, regenerate to compare
@@ -480,7 +488,7 @@
ScratchFile tmp;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
- bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store);
+ bool success = WriteElf(tmp.GetFile(), dex_files, key_value_store, false);
ASSERT_TRUE(success);
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
@@ -494,7 +502,15 @@
EXPECT_LT(static_cast<size_t>(oat_file->Size()), static_cast<size_t>(tmp.GetFile()->GetLength()));
}
-TEST_F(OatTest, DexFileInput) {
+static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>& data) {
+ // If in verify mode (= fail the verifier mode), make sure we fail early. We'll fail already
+ // because of the missing map, but that may lead to out of bounds reads.
+ if (verify) {
+ const_cast<DexFile::Header*>(&data->GetHeader())->checksum_++;
+ }
+}
+
+void OatTest::TestDexFileInput(bool verify) {
TimingLogger timings("OatTest::DexFileInput", false, false);
std::vector<const char*> input_filenames;
@@ -504,6 +520,9 @@
builder1.AddField("Lsome.TestClass;", "int", "someField");
builder1.AddMethod("Lsome.TestClass;", "()I", "foo");
std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
dex_file1_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -516,6 +535,9 @@
builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
dex_file2_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -526,7 +548,14 @@
ScratchFile oat_file;
SafeMap<std::string, std::string> key_value_store;
key_value_store.Put(OatHeader::kImageLocationKey, "test.art");
- success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
+ success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
+
+ // In verify mode, we expect failure.
+ if (verify) {
+ ASSERT_FALSE(success);
+ return;
+ }
+
ASSERT_TRUE(success);
std::string error_msg;
@@ -557,7 +586,15 @@
ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation());
}
-TEST_F(OatTest, ZipFileInput) {
+TEST_F(OatTest, DexFileInputCheckOutput) {
+ TestDexFileInput(false);
+}
+
+TEST_F(OatTest, DexFileInputCheckVerifier) {
+ TestDexFileInput(true);
+}
+
+void OatTest::TestZipFileInput(bool verify) {
TimingLogger timings("OatTest::DexFileInput", false, false);
ScratchFile zip_file;
@@ -568,6 +605,9 @@
builder1.AddField("Lsome.TestClass;", "long", "someField");
builder1.AddMethod("Lsome.TestClass;", "()D", "foo");
std::unique_ptr<const DexFile> dex_file1_data = builder1.Build(dex_file1.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file1_data);
+
bool success = dex_file1.GetFile()->WriteFully(&dex_file1_data->GetHeader(),
dex_file1_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -583,6 +623,9 @@
builder2.AddField("Land.AnotherTestClass;", "boolean", "someOtherField");
builder2.AddMethod("Land.AnotherTestClass;", "()J", "bar");
std::unique_ptr<const DexFile> dex_file2_data = builder2.Build(dex_file2.GetFilename());
+
+ MaybeModifyDexFileToFail(verify, dex_file2_data);
+
success = dex_file2.GetFile()->WriteFully(&dex_file2_data->GetHeader(),
dex_file2_data->GetHeader().file_size_);
ASSERT_TRUE(success);
@@ -603,37 +646,42 @@
std::vector<const char*> input_filenames { zip_file.GetFilename().c_str() }; // NOLINT [readability/braces] [4]
ScratchFile oat_file;
- success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store);
- ASSERT_TRUE(success);
+ success = WriteElf(oat_file.GetFile(), input_filenames, key_value_store, verify);
- std::string error_msg;
- std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
- oat_file.GetFilename(),
- nullptr,
- nullptr,
- false,
- nullptr,
- &error_msg));
- ASSERT_TRUE(opened_oat_file != nullptr);
- ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
- std::unique_ptr<const DexFile> opened_dex_file1 =
- opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
- std::unique_ptr<const DexFile> opened_dex_file2 =
- opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
- ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
- &opened_dex_file1->GetHeader(),
- dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
- opened_dex_file1->GetLocation());
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
- ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
- &opened_dex_file2->GetHeader(),
- dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
- opened_dex_file2->GetLocation());
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
+
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
}
{
@@ -645,38 +693,51 @@
success = WriteElf(oat_file.GetFile(),
std::move(zip_fd),
zip_file.GetFilename().c_str(),
- key_value_store);
- ASSERT_TRUE(success);
+ key_value_store,
+ verify);
+ if (verify) {
+ ASSERT_FALSE(success);
+ } else {
+ ASSERT_TRUE(success);
- std::string error_msg;
- std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
- oat_file.GetFilename(),
- nullptr,
- nullptr,
- false,
- nullptr,
- &error_msg));
- ASSERT_TRUE(opened_oat_file != nullptr);
- ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
- std::unique_ptr<const DexFile> opened_dex_file1 =
- opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
- std::unique_ptr<const DexFile> opened_dex_file2 =
- opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
+ std::string error_msg;
+ std::unique_ptr<OatFile> opened_oat_file(OatFile::Open(oat_file.GetFilename(),
+ oat_file.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ nullptr,
+ &error_msg));
+ ASSERT_TRUE(opened_oat_file != nullptr);
+ ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
+ std::unique_ptr<const DexFile> opened_dex_file1 =
+ opened_oat_file->GetOatDexFiles()[0]->OpenDexFile(&error_msg);
+ std::unique_ptr<const DexFile> opened_dex_file2 =
+ opened_oat_file->GetOatDexFiles()[1]->OpenDexFile(&error_msg);
- ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
- &opened_dex_file1->GetHeader(),
- dex_file1_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
- opened_dex_file1->GetLocation());
+ ASSERT_EQ(dex_file1_data->GetHeader().file_size_, opened_dex_file1->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file1_data->GetHeader(),
+ &opened_dex_file1->GetHeader(),
+ dex_file1_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(0, zip_file.GetFilename().c_str()),
+ opened_dex_file1->GetLocation());
- ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
- ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
- &opened_dex_file2->GetHeader(),
- dex_file2_data->GetHeader().file_size_));
- ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
- opened_dex_file2->GetLocation());
+ ASSERT_EQ(dex_file2_data->GetHeader().file_size_, opened_dex_file2->GetHeader().file_size_);
+ ASSERT_EQ(0, memcmp(&dex_file2_data->GetHeader(),
+ &opened_dex_file2->GetHeader(),
+ dex_file2_data->GetHeader().file_size_));
+ ASSERT_EQ(DexFile::GetMultiDexLocation(1, zip_file.GetFilename().c_str()),
+ opened_dex_file2->GetLocation());
+ }
}
}
+TEST_F(OatTest, ZipFileInputCheckOutput) {
+ TestZipFileInput(false);
+}
+
+TEST_F(OatTest, ZipFileInputCheckVerifier) {
+ TestZipFileInput(true);
+}
+
} // namespace art
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index a542162..90ac499 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -397,6 +397,7 @@
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
CHECK(write_state_ == WriteState::kAddingDexFileSources);
@@ -424,7 +425,7 @@
}
if (!WriteOatDexFiles(rodata) ||
!ExtendForTypeLookupTables(rodata, file, size_after_type_lookup_tables) ||
- !OpenDexFiles(file, &dex_files_map, &dex_files) ||
+ !OpenDexFiles(file, verify, &dex_files_map, &dex_files) ||
!WriteTypeLookupTables(dex_files_map.get(), dex_files)) {
return false;
}
@@ -806,7 +807,7 @@
}
}
- if (writer_->compiler_driver_->GetCompilerOptions().GetGenerateDebugInfo()) {
+ if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) {
// Record debug information for this function if we are doing that.
const uint32_t quick_code_start = quick_code_offset -
writer_->oat_header_->GetExecutableOffset() - thumb_offset;
@@ -2143,6 +2144,7 @@
bool OatWriter::OpenDexFiles(
File* file,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
TimingLogger::ScopedTiming split("OpenDexFiles", timings_);
@@ -2201,9 +2203,11 @@
oat_dex_file.GetLocation(),
oat_dex_file.dex_file_location_checksum_,
/* oat_dex_file */ nullptr,
+ verify,
&error_msg));
if (dex_files.back() == nullptr) {
- LOG(ERROR) << "Failed to open dex file from oat file. File:" << oat_dex_file.GetLocation();
+ LOG(ERROR) << "Failed to open dex file from oat file. File: " << oat_dex_file.GetLocation()
+ << " Error: " << error_msg;
return false;
}
}
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index d681998..14c6d50 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -139,12 +139,15 @@
CreateTypeLookupTable create_type_lookup_table = CreateTypeLookupTable::kDefault);
dchecked_vector<const char*> GetSourceLocations() const;
- // Write raw dex files to the .rodata section and open them from the oat file.
+ // Write raw dex files to the .rodata section and open them from the oat file. The verify
+ // setting dictates whether the dex file verifier should check the dex files. This is generally
+ // the case, and should only be false for tests.
bool WriteAndOpenDexFiles(OutputStream* rodata,
File* file,
InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
SafeMap<std::string, std::string>* key_value_store,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
// Prepare layout of remaining data.
@@ -258,6 +261,7 @@
bool WriteOatDexFiles(OutputStream* rodata);
bool ExtendForTypeLookupTables(OutputStream* rodata, File* file, size_t offset);
bool OpenDexFiles(File* file,
+ bool verify,
/*out*/ std::unique_ptr<MemMap>* opened_dex_files_map,
/*out*/ std::vector<std::unique_ptr<const DexFile>>* opened_dex_files);
bool WriteTypeLookupTables(MemMap* opened_dex_files_map,
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 2805162..9d796c1 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -507,6 +507,7 @@
|| IsPass(HDeadCodeElimination::kFinalDeadCodeEliminationPassName)
|| IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName)
|| IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName)
+ || IsPass(RegisterAllocator::kRegisterAllocatorPassName)
|| IsPass(SsaBuilder::kSsaBuilderPassName)) {
HLoopInformation* info = instruction->GetBlock()->GetLoopInformation();
if (info == nullptr) {
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index fffd005..3fac914 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -676,7 +676,7 @@
return nullptr;
}
codegen->GetAssembler()->cfi().SetEnabled(
- compiler_driver->GetCompilerOptions().GetGenerateDebugInfo());
+ compiler_driver->GetCompilerOptions().GenerateAnyDebugInfo());
PassObserver pass_observer(graph,
codegen.get(),
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index d77639d..5cd30ad 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -179,7 +179,7 @@
}
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// By blocking all registers at the top of each catch block or irreducible loop, we force
// intervals belonging to the live-in set of the catch/header block to be spilled.
// TODO(ngeoffray): Phis in this block could be allocated in register.
@@ -1749,8 +1749,10 @@
}
// Find the intervals that cover `from` and `to`.
- LiveInterval* destination = interval->GetSiblingAt(to->GetLifetimeStart());
- LiveInterval* source = interval->GetSiblingAt(from->GetLifetimeEnd() - 1);
+ size_t destination_position = to->GetLifetimeStart();
+ size_t source_position = from->GetLifetimeEnd() - 1;
+ LiveInterval* destination = interval->GetSiblingAt(destination_position);
+ LiveInterval* source = interval->GetSiblingAt(source_position);
if (destination == source) {
// Interval was not split.
@@ -1759,7 +1761,8 @@
LiveInterval* parent = interval->GetParent();
HInstruction* defined_by = parent->GetDefinedBy();
- if (destination == nullptr) {
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (destination == nullptr || !destination->CoversSlow(destination_position))) {
// Our live_in fixed point calculation has found that the instruction is live
// in the `to` block because it will eventually enter an irreducible loop. Our
// live interval computation however does not compute a fixed point, and
@@ -1775,18 +1778,41 @@
return;
}
+ Location location_source;
+ // `GetSiblingAt` returns the interval whose start and end cover `position`,
+ // but does not check whether the interval is inactive at that position.
+ // The only situation where the interval is inactive at that position is in the
+ // presence of irreducible loops for constants and ArtMethod.
+ if (codegen_->GetGraph()->HasIrreducibleLoops() &&
+ (source == nullptr || !source->CoversSlow(source_position))) {
+ DCHECK(IsMaterializableEntryBlockInstructionOfGraphWithIrreducibleLoop(defined_by));
+ if (defined_by->IsConstant()) {
+ location_source = defined_by->GetLocations()->Out();
+ } else {
+ DCHECK(defined_by->IsCurrentMethod());
+ location_source = parent->NeedsTwoSpillSlots()
+ ? Location::DoubleStackSlot(parent->GetSpillSlot())
+ : Location::StackSlot(parent->GetSpillSlot());
+ }
+ } else {
+ DCHECK(source != nullptr);
+ DCHECK(source->CoversSlow(source_position));
+ DCHECK(destination->CoversSlow(destination_position));
+ location_source = source->ToLocation();
+ }
+
// If `from` has only one successor, we can put the moves at the exit of it. Otherwise
// we need to put the moves at the entry of `to`.
if (from->GetNormalSuccessors().size() == 1) {
InsertParallelMoveAtExitOf(from,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
} else {
DCHECK_EQ(to->GetPredecessors().size(), 1u);
InsertParallelMoveAtEntryOf(to,
defined_by,
- source->ToLocation(),
+ location_source,
destination->ToLocation());
}
}
@@ -1890,7 +1916,7 @@
for (HLinearOrderIterator it(*codegen_->GetGraph()); !it.Done(); it.Advance()) {
HBasicBlock* block = it.Current();
if (block->IsCatchBlock() ||
- (block->GetLoopInformation() != nullptr && block->GetLoopInformation()->IsIrreducible())) {
+ (block->IsLoopHeader() && block->GetLoopInformation()->IsIrreducible())) {
// Instructions live at the top of catch blocks or irreducible loop header
// were forced to spill.
if (kIsDebugBuild) {
diff --git a/compiler/utils/test_dex_file_builder.h b/compiler/utils/test_dex_file_builder.h
index e57a540..2958dc6 100644
--- a/compiler/utils/test_dex_file_builder.h
+++ b/compiler/utils/test_dex_file_builder.h
@@ -89,11 +89,12 @@
DexFile::Header* header = reinterpret_cast<DexFile::Header*>(&header_data.data);
std::copy_n(DexFile::kDexMagic, 4u, header->magic_);
std::copy_n(DexFile::kDexMagicVersion, 4u, header->magic_ + 4u);
- header->header_size_ = sizeof(header);
+ header->header_size_ = sizeof(DexFile::Header);
header->endian_tag_ = DexFile::kDexEndianConstant;
header->link_size_ = 0u; // Unused.
header->link_off_ = 0u; // Unused.
- header->map_off_ = 0u; // Unused.
+ header->map_off_ = 0u; // Unused. TODO: This is wrong. Dex files created by this builder
+ // cannot be verified. b/26808512
uint32_t data_section_size = 0u;
@@ -213,13 +214,22 @@
// Leave signature as zeros.
header->file_size_ = dex_file_data_.size();
+
+ // Write the complete header early, as part of it needs to be checksummed.
+ std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
+
+ // Checksum starts after the checksum field.
size_t skip = sizeof(header->magic_) + sizeof(header->checksum_);
- header->checksum_ = adler32(0u, dex_file_data_.data() + skip, dex_file_data_.size() - skip);
+ header->checksum_ = adler32(adler32(0L, Z_NULL, 0),
+ dex_file_data_.data() + skip,
+ dex_file_data_.size() - skip);
+
+ // Write the complete header again, just simpler that way.
std::memcpy(&dex_file_data_[0], header_data.data, sizeof(DexFile::Header));
std::string error_msg;
std::unique_ptr<const DexFile> dex_file(DexFile::Open(
- &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, &error_msg));
+ &dex_file_data_[0], dex_file_data_.size(), dex_location, 0u, nullptr, false, &error_msg));
CHECK(dex_file != nullptr) << error_msg;
return dex_file;
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 86f51e1..bbed09c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -318,6 +318,11 @@
UsageError("");
UsageError(" --no-generate-debug-info: Do not generate debug information for native debugging.");
UsageError("");
+ UsageError(" --generate-mini-debug-info: Generate minimal amount of LZMA-compressed");
+ UsageError(" debug information necessary to print backtraces. (disabled by default)");
+ UsageError("");
+ UsageError(" --no-generate-mini-debug-info: Do do generated backtrace info.");
+ UsageError("");
UsageError(" --debuggable: Produce code debuggable with Java debugger.");
UsageError("");
UsageError(" --native-debuggable: Produce code debuggable with native debugger (like LLDB).");
@@ -548,13 +553,15 @@
driver_(nullptr),
opened_dex_files_maps_(),
opened_dex_files_(),
+ no_inline_from_dex_files_(),
dump_stats_(false),
dump_passes_(false),
dump_timing_(false),
dump_slow_timing_(kIsDebugBuild),
swap_fd_(-1),
app_image_fd_(kInvalidFd),
- timings_(timings) {}
+ timings_(timings),
+ force_determinism_(false) {}
~Dex2Oat() {
// Log completion time before deleting the runtime_, because this accesses
@@ -916,6 +923,12 @@
// Fill some values into the key-value store for the oat header.
key_value_store_.reset(new SafeMap<std::string, std::string>());
+
+ // Automatically force determinism for the boot image in a host build.
+ if (!kIsTargetBuild && IsBootImage()) {
+ force_determinism_ = true;
+ }
+ compiler_options_->force_determinism_ = force_determinism_;
}
void ExpandOatAndImageFilenames() {
@@ -1159,6 +1172,8 @@
multi_image_ = true;
} else if (option.starts_with("--no-inline-from=")) {
no_inline_from_string_ = option.substr(strlen("--no-inline-from=")).data();
+ } else if (option == "--force-determinism") {
+ force_determinism_ = true;
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1351,6 +1366,7 @@
instruction_set_,
instruction_set_features_.get(),
key_value_store_.get(),
+ /* verify */ true,
&opened_dex_files_map,
&opened_dex_files)) {
return false;
@@ -1452,14 +1468,18 @@
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
compiler_phases_timings_.reset(new CumulativeLogger("compilation times"));
- // Find the dex file we should not inline from.
+ // Find the dex files we should not inline from.
+
+ std::vector<std::string> no_inline_filters;
+ Split(no_inline_from_string_, ',', &no_inline_filters);
// For now, on the host always have core-oj removed.
- if (!kIsTargetBuild && no_inline_from_string_.empty()) {
- no_inline_from_string_ = "core-oj";
+ const std::string core_oj = "core-oj";
+ if (!kIsTargetBuild && !ContainsElement(no_inline_filters, core_oj)) {
+ no_inline_filters.push_back(core_oj);
}
- if (!no_inline_from_string_.empty()) {
+ if (!no_inline_filters.empty()) {
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
std::vector<const std::vector<const DexFile*>*> dex_file_vectors = {
@@ -1468,34 +1488,30 @@
&dex_files_
};
for (const std::vector<const DexFile*>* dex_file_vector : dex_file_vectors) {
- if (dex_file_vector == nullptr) {
- continue;
- }
-
- bool found = false;
-
for (const DexFile* dex_file : *dex_file_vector) {
- // Try the complete location first.
- found = no_inline_from_string_ == dex_file->GetLocation();
- // The try just the name.
- if (!found) {
- size_t last_slash = dex_file->GetLocation().rfind('/');
- if (last_slash != std::string::npos) {
- found = StartsWith(dex_file->GetLocation().substr(last_slash + 1),
- no_inline_from_string_.c_str());
+ for (const std::string& filter : no_inline_filters) {
+ // Use dex_file->GetLocation() rather than dex_file->GetBaseLocation(). This
+ // allows tests to specify <test-dexfile>:classes2.dex if needed but if the
+ // base location passes the StartsWith() test, so do all extra locations.
+ std::string dex_location = dex_file->GetLocation();
+ if (filter.find('/') == std::string::npos) {
+ // The filter does not contain the path. Remove the path from dex_location as well.
+ size_t last_slash = dex_file->GetLocation().rfind('/');
+ if (last_slash != std::string::npos) {
+ dex_location = dex_location.substr(last_slash + 1);
+ }
+ }
+
+ if (StartsWith(dex_location, filter.c_str())) {
+ VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
+ no_inline_from_dex_files_.push_back(dex_file);
+ break;
}
}
-
- if (found) {
- VLOG(compiler) << "Disabling inlining from " << dex_file->GetLocation();
- compiler_options_->no_inline_from_ = dex_file;
- break;
- }
}
-
- if (found) {
- break;
- }
+ }
+ if (!no_inline_from_dex_files_.empty()) {
+ compiler_options_->no_inline_from_ = &no_inline_from_dex_files_;
}
}
@@ -2105,6 +2121,21 @@
// foreground collector by default for dex2oat.
raw_options.push_back(std::make_pair("-XX:DisableHSpaceCompactForOOM", nullptr));
+ // If we're asked to be deterministic, ensure non-concurrent GC for determinism. Also
+ // force the free-list implementation for large objects.
+ if (compiler_options_->IsForceDeterminism()) {
+ raw_options.push_back(std::make_pair("-Xgc:nonconcurrent", nullptr));
+ raw_options.push_back(std::make_pair("-XX:LargeObjectSpace=freelist", nullptr));
+
+ // We also need to turn off the nonmoving space. For that, we need to disable HSpace
+ // compaction (done above) and ensure that neither foreground nor background collectors
+ // are concurrent.
+ raw_options.push_back(std::make_pair("-XX:BackgroundGC=nonconcurrent", nullptr));
+
+ // To make identity hashcode deterministic, set a known seed.
+ mirror::Object::SetHashCodeSeed(987654321U);
+ }
+
if (!Runtime::ParseOptions(raw_options, false, runtime_options)) {
LOG(ERROR) << "Failed to parse runtime options";
return false;
@@ -2384,6 +2415,8 @@
std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
+ std::vector<const DexFile*> no_inline_from_dex_files_;
+
std::vector<std::string> verbose_methods_;
bool dump_stats_;
bool dump_passes_;
@@ -2406,6 +2439,9 @@
// Backing storage.
std::vector<std::string> char_backing_storage_;
+ // See CompilerOptions.force_determinism_.
+ bool force_determinism_;
+
DISALLOW_IMPLICIT_CONSTRUCTORS(Dex2Oat);
};
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 57b52fc..a1f6eee 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -142,9 +142,7 @@
text->End();
if (oat_file_->BssSize() != 0) {
- bss->Start();
- bss->SetSize(oat_file_->BssSize());
- bss->End();
+ bss->WriteNoBitsSection(oat_file_->BssSize());
}
builder_->WriteDynamicSection(elf_file->GetPath());
@@ -1656,7 +1654,7 @@
stats_.file_bytes = file->GetLength();
}
size_t header_bytes = sizeof(ImageHeader);
- const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+ const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
const auto& field_section = image_header_.GetImageSection(ImageHeader::kSectionArtFields);
const auto& method_section = image_header_.GetMethodsSection();
const auto& dex_cache_arrays_section = image_header_.GetImageSection(
@@ -1665,17 +1663,46 @@
ImageHeader::kSectionInternedStrings);
const auto& class_table_section = image_header_.GetImageSection(
ImageHeader::kSectionClassTable);
+ const auto& bitmap_section = image_header_.GetImageSection(ImageHeader::kSectionImageBitmap);
+
stats_.header_bytes = header_bytes;
- stats_.alignment_bytes += RoundUp(header_bytes, kObjectAlignment) - header_bytes;
- // Add padding between the field and method section.
- // (Field section is 4-byte aligned, method section is 8-byte aligned on 64-bit targets.)
- stats_.alignment_bytes += method_section.Offset() -
- (field_section.Offset() + field_section.Size());
- // Add padding between the dex cache arrays section and the intern table. (Dex cache
- // arrays section is 4-byte aligned on 32-bit targets, intern table is 8-byte aligned.)
- stats_.alignment_bytes += intern_section.Offset() -
- (dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size());
- stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
+
+ // Objects are kObjectAlignment-aligned.
+ // CHECK_EQ(RoundUp(header_bytes, kObjectAlignment), object_section.Offset());
+ if (object_section.Offset() > header_bytes) {
+ stats_.alignment_bytes += object_section.Offset() - header_bytes;
+ }
+
+ // Field section is 4-byte aligned.
+ constexpr size_t kFieldSectionAlignment = 4U;
+ uint32_t end_objects = object_section.Offset() + object_section.Size();
+ CHECK_EQ(RoundUp(end_objects, kFieldSectionAlignment), field_section.Offset());
+ stats_.alignment_bytes += field_section.Offset() - end_objects;
+
+ // Method section is 4/8 byte aligned depending on target. Just check for 4-byte alignment.
+ uint32_t end_fields = field_section.Offset() + field_section.Size();
+ CHECK_ALIGNED(method_section.Offset(), 4);
+ stats_.alignment_bytes += method_section.Offset() - end_fields;
+
+ // Dex cache arrays section is aligned depending on the target. Just check for 4-byte alignment.
+ uint32_t end_methods = method_section.Offset() + method_section.Size();
+ CHECK_ALIGNED(dex_cache_arrays_section.Offset(), 4);
+ stats_.alignment_bytes += dex_cache_arrays_section.Offset() - end_methods;
+
+ // Intern table is 8-byte aligned.
+ uint32_t end_caches = dex_cache_arrays_section.Offset() + dex_cache_arrays_section.Size();
+ CHECK_EQ(RoundUp(end_caches, 8U), intern_section.Offset());
+ stats_.alignment_bytes += intern_section.Offset() - end_caches;
+
+ // Add space between intern table and class table.
+ uint32_t end_intern = intern_section.Offset() + intern_section.Size();
+ stats_.alignment_bytes += class_table_section.Offset() - end_intern;
+
+ // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
+ uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+ CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
+ stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
stats_.art_method_bytes += method_section.Size();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index bb709e8..44cc9b7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1533,6 +1533,14 @@
}
}
if (!equal) {
+ VLOG(image) << "Image dex files " << image_dex_file_names.size();
+ for (mirror::String* name : image_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
+ VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+ for (mirror::String* name : loader_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
+ }
*error_msg = "Rejecting application image due to class loader mismatch";
return false;
}
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 9b93c13..81a3e4b 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -195,6 +195,30 @@
}
}
+std::unique_ptr<const DexFile> DexFile::Open(const uint8_t* base, size_t size,
+ const std::string& location,
+ uint32_t location_checksum,
+ const OatDexFile* oat_dex_file,
+ bool verify,
+ std::string* error_msg) {
+ std::unique_ptr<const DexFile> dex_file = OpenMemory(base,
+ size,
+ location,
+ location_checksum,
+ nullptr,
+ oat_dex_file,
+ error_msg);
+ if (verify && !DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ location.c_str(),
+ error_msg)) {
+ return nullptr;
+ }
+
+ return dex_file;
+}
+
std::unique_ptr<const DexFile> DexFile::OpenFile(int fd, const char* location, bool verify,
std::string* error_msg) {
CHECK(location != nullptr);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 200121e..e497e9c 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -417,9 +417,8 @@
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
- std::string* error_msg) {
- return OpenMemory(base, size, location, location_checksum, nullptr, oat_dex_file, error_msg);
- }
+ bool verify,
+ std::string* error_msg);
// Open all classesXXX.dex files from a zip archive.
static bool OpenFromZip(const ZipArchive& zip_archive, const std::string& location,
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 137540a..84483b4 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -114,6 +114,9 @@
// timeout on how long we wait for finalizers to run. b/21544853
static constexpr uint64_t kNativeAllocationFinalizeTimeout = MsToNs(250u);
+// For deterministic compilation, we need the heap to be at a well-known address.
+static constexpr uint32_t kAllocSpaceBeginForDeterministicAoT = 0x40000000;
+
Heap::Heap(size_t initial_size,
size_t growth_limit,
size_t min_free,
@@ -347,11 +350,16 @@
bool separate_non_moving_space = is_zygote ||
support_homogeneous_space_compaction || IsMovingGc(foreground_collector_type_) ||
IsMovingGc(background_collector_type_);
- if (foreground_collector_type == kCollectorTypeGSS) {
+ if (foreground_collector_type_ == kCollectorTypeGSS) {
separate_non_moving_space = false;
}
std::unique_ptr<MemMap> main_mem_map_1;
std::unique_ptr<MemMap> main_mem_map_2;
+
+ // Gross hack to make dex2oat deterministic.
+ if (requested_alloc_space_begin == nullptr && Runtime::Current()->IsAotCompiler()) {
+ requested_alloc_space_begin = reinterpret_cast<uint8_t*>(kAllocSpaceBeginForDeterministicAoT);
+ }
uint8_t* request_begin = requested_alloc_space_begin;
if (request_begin != nullptr && separate_non_moving_space) {
request_begin += non_moving_space_capacity;
@@ -2346,6 +2354,9 @@
// We still want to GC in case there is some unreachable non moving objects that could cause a
// suboptimal bin packing when we compact the zygote space.
CollectGarbageInternal(collector::kGcTypeFull, kGcCauseBackground, false);
+ // Trim the pages at the end of the non moving space. Trim while not holding zygote lock since
+ // the trim process may require locking the mutator lock.
+ non_moving_space_->Trim();
}
Thread* self = Thread::Current();
MutexLock mu(self, zygote_creation_lock_);
@@ -2356,8 +2367,6 @@
Runtime::Current()->GetInternTable()->AddNewTable();
Runtime::Current()->GetClassLinker()->MoveClassTableToPreZygote();
VLOG(heap) << "Starting PreZygoteFork";
- // Trim the pages at the end of the non moving space.
- non_moving_space_->Trim();
// The end of the non-moving space may be protected, unprotect it so that we can copy the zygote
// there.
non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index b6f424b..b3439f7 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -381,19 +381,26 @@
return (T)static_cast<uintptr_t>(AsIntArray()->GetWithoutChecks(idx));
}
-template<bool kTransactionActive, bool kUnchecked, typename T>
-inline void PointerArray::SetElementPtrSize(uint32_t idx, T element, size_t ptr_size) {
+template<bool kTransactionActive, bool kUnchecked>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size) {
if (ptr_size == 8) {
(kUnchecked ? down_cast<LongArray*>(static_cast<Object*>(this)) : AsLongArray())->
- SetWithoutChecks<kTransactionActive>(idx, (uint64_t)(element));
+ SetWithoutChecks<kTransactionActive>(idx, element);
} else {
DCHECK_EQ(ptr_size, 4u);
- DCHECK_LE((uintptr_t)element, 0xFFFFFFFFu);
+ DCHECK_LE(element, static_cast<uint64_t>(0xFFFFFFFFu));
(kUnchecked ? down_cast<IntArray*>(static_cast<Object*>(this)) : AsIntArray())
- ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>((uintptr_t)element));
+ ->SetWithoutChecks<kTransactionActive>(idx, static_cast<uint32_t>(element));
}
}
+template<bool kTransactionActive, bool kUnchecked, typename T>
+inline void PointerArray::SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size) {
+ SetElementPtrSize<kTransactionActive, kUnchecked>(idx,
+ reinterpret_cast<uintptr_t>(element),
+ ptr_size);
+}
+
template <typename Visitor>
inline void PointerArray::Fixup(mirror::PointerArray* dest,
size_t pointer_size,
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 50d77eb..2bd6c5b 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -187,8 +187,11 @@
T GetElementPtrSize(uint32_t idx, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
+ template<bool kTransactionActive = false, bool kUnchecked = false>
+ void SetElementPtrSize(uint32_t idx, uint64_t element, size_t ptr_size)
+ SHARED_REQUIRES(Locks::mutator_lock_);
template<bool kTransactionActive = false, bool kUnchecked = false, typename T>
- void SetElementPtrSize(uint32_t idx, T element, size_t ptr_size)
+ void SetElementPtrSize(uint32_t idx, T* element, size_t ptr_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Fixup the pointers in the dest arrays by passing our pointers through the visitor. Only copies
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 75a3f1a..d5783c0 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -157,7 +157,7 @@
inline LengthPrefixedArray<ArtMethod>* Class::GetMethodsPtr() {
return reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
- GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_)));
+ static_cast<uintptr_t>(GetField64(OFFSET_OF_OBJECT_MEMBER(Class, methods_))));
}
template<VerifyObjectFlags kVerifyFlags>
@@ -208,7 +208,7 @@
inline void Class::SetMethodsPtrInternal(LengthPrefixedArray<ArtMethod>* new_methods) {
SetField64<false>(OFFSET_OF_OBJECT_MEMBER(Class, methods_),
- reinterpret_cast<uint64_t>(new_methods));
+ static_cast<uint64_t>(reinterpret_cast<uintptr_t>(new_methods)));
}
template<VerifyObjectFlags kVerifyFlags>
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 3017820..195bbc9 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -561,7 +561,7 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(size_t pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 69;
+ uint32_t vtable_entries = Object::kVTableLength + 72;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 0ddd4a2..a80585a 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -469,14 +469,21 @@
return soa.AddLocalReference<jobjectArray>(ret.Get());
}
-static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationType) {
+static jobject Class_getDeclaredAnnotation(JNIEnv* env, jobject javaThis, jclass annotationClass) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<2> hs(soa.Self());
Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+
+ // Handle public contract to throw NPE if the "annotationClass" argument was null.
+ if (UNLIKELY(annotationClass == nullptr)) {
+ ThrowNullPointerException("annotationClass");
+ return nullptr;
+ }
+
if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
return nullptr;
}
- Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationType)));
+ Handle<mirror::Class> annotation_class(hs.NewHandle(soa.Decode<mirror::Class*>(annotationClass)));
return soa.AddLocalReference<jobject>(
klass->GetDexFile().GetAnnotationForClass(klass, annotation_class));
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 82b3933..8f321a0 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1072,8 +1072,13 @@
}
std::unique_ptr<const DexFile> OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const {
- return DexFile::Open(dex_file_pointer_, FileSize(), dex_file_location_,
- dex_file_location_checksum_, this, error_msg);
+ return DexFile::Open(dex_file_pointer_,
+ FileSize(),
+ dex_file_location_,
+ dex_file_location_checksum_,
+ this,
+ false /* verify */,
+ error_msg);
}
uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
diff --git a/test/048-reflect-v8/expected.txt b/test/048-reflect-v8/expected.txt
index 2d0b4cc..3109ecc 100644
--- a/test/048-reflect-v8/expected.txt
+++ b/test/048-reflect-v8/expected.txt
@@ -1,4 +1,95 @@
-Main$DefaultInterface is default = yes
-Main$RegularInterface is default = no
-Main$ImplementsWithDefault is default = yes
-Main$ImplementsWithRegular is default = no
+==============================
+Are These Methods Default:
+==============================
+IsDefaultTest$DefaultInterface is default = yes
+IsDefaultTest$RegularInterface is default = no
+IsDefaultTest$ImplementsWithDefault is default = yes
+IsDefaultTest$ImplementsWithRegular is default = no
+==============================
+Class annotations by type:
+==============================
+Annotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Annotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Annotations by type, defined by class UserSub with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by class UserSub with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by class UserSub2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Class declared annotation:
+==============================
+Declared annotations by class class SingleUser, annotation interface Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annotations by class class SingleUser, annotation interface Calendars: <null>
+Declared annotations by class class User, annotation interface Calendar: <null>
+Declared annotations by class class User, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annotations by class class UserComplex, annotation interface Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserComplex, annotation interface Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annotations by class class UserSub, annotation interface Calendar: <null>
+Declared annotations by class class UserSub, annotation interface Calendars: <null>
+Declared annotations by class class UserSub2, annotation interface Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annotations by class class UserSub2, annotation interface Calendars: <null>
+-----------------------------
+-----------------------------
+==============================
+Declared class annotations by type:
+==============================
+Declared annnotations by type, defined by class SingleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Declared annnotations by type, defined by class SingleUser with annotation Calendars: <empty>
+Declared annnotations by type, defined by class User with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Declared annnotations by type, defined by class User with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Declared annnotations by type, defined by class User2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class User2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Declared annnotations by type, defined by class UserComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Declared annnotations by type, defined by class UserComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+Declared annnotations by type, defined by class UserSub with annotation Calendar: <empty>
+Declared annnotations by type, defined by class UserSub with annotation Calendars: <empty>
+Declared annnotations by type, defined by class UserSub2 with annotation Calendar: @Calendar(dayOfMonth=sub2, dayOfWeek=unspecified_week, hour=6)
+Declared annnotations by type, defined by class UserSub2 with annotation Calendars: <empty>
+-----------------------------
+-----------------------------
+==============================
+Method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations:
+==============================
+Annotations declared by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations declared by method singleUser with annotation Calendars: <null>
+Annotations declared by method user with annotation Calendar: <null>
+Annotations declared by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations declared by method user2 with annotation Calendar: <null>
+Annotations declared by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations declared by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6)
+Annotations declared by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
+==============================
+Declared method annotations by type:
+==============================
+Annotations by type, defined by method singleUser with annotation Calendar: @Calendar(dayOfMonth=unspecified_month, dayOfWeek=single, hour=23)
+Annotations by type, defined by method singleUser with annotation Calendars: <empty>
+Annotations by type, defined by method user with annotation Calendar: @Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)
+Annotations by type, defined by method user with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=last, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=unspecified_month, dayOfWeek=Fri, hour=23)])
+Annotations by type, defined by method user2 with annotation Calendar: @Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)
+Annotations by type, defined by method user2 with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=z, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=x, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=y, dayOfWeek=unspecified_week, hour=6)])
+Annotations by type, defined by method userComplex with annotation Calendar: @Calendar(dayOfMonth=afirst, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)
+Annotations by type, defined by method userComplex with annotation Calendars: @Calendars(value=[@Calendar(dayOfMonth=zsecond, dayOfWeek=unspecified_week, hour=6), @Calendar(dayOfMonth=athird, dayOfWeek=unspecified_week, hour=23)])
+-----------------------------
+-----------------------------
diff --git a/test/048-reflect-v8/src/AnnotationTest.java b/test/048-reflect-v8/src/AnnotationTest.java
new file mode 100644
index 0000000..75e6845
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+
+public class AnnotationTest extends AnnotationTestHelpers {
+ public static void testAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Class annotations by type:");
+ System.out.println("==============================");
+
+ // Print associated annotations:
+ // * A is directly present or repeatably present on an element E;
+ // * No annotation of A is directly/repeatably present on an element
+ // AND E is a class AND A's type is inheritable, AND A is associated with its superclass.
+ // (Looks through subtypes recursively only if there's 0 result at each level,
+ // and the annotation is @Inheritable).
+ printAnnotationsByType(Calendar.class, SingleUser.class);
+ printAnnotationsByType(Calendars.class, SingleUser.class);
+
+ printAnnotationsByType(Calendar.class, User.class);
+ printAnnotationsByType(Calendars.class, User.class);
+
+ printAnnotationsByType(Calendar.class, User2.class); // Enforce ordering 'z,x,y'
+ printAnnotationsByType(Calendars.class, User2.class);
+
+ // NOTE:
+ // Order of outer-most annotations Calendars[C,C],S vs C,Calendars[C,C] is unspecified.
+ // In particular it's the order of #getDeclaredAnnotations which is completely unmentioned.
+ // The only requirement for #getAnnotationsByType is to have same ordering as
+ // #getDeclaredAnnotations.
+ // (Calendars[] itself has to maintain value() order).
+ printAnnotationsByType(Calendar.class, UserComplex.class); // Cs(C,C),C collapses into C,C,C.
+ printAnnotationsByType(Calendars.class, UserComplex.class);
+
+ printAnnotationsByType(Calendar.class, UserSub.class);
+ printAnnotationsByType(Calendars.class, UserSub.class);
+
+ printAnnotationsByType(Calendar.class, UserSub2.class);
+ // The directly present "Calendar" annotation masks all the repeatably present
+ // "Calendar" annotations coming from User.
+ printAnnotationsByType(Calendars.class, UserSub2.class);
+ // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+ // so it doesn't mask the "User" Calendars annotation.
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+
+ }
+
+ public static void testDeclaredAnnotation() {
+ System.out.println("==============================");
+ System.out.println("Class declared annotation:");
+ System.out.println("==============================");
+
+ // Print directly present annotations:
+ //
+ // The element E has an annotation_item for it (accessible through an
+ // annotations_directory_item) corresponding to an annotation A,
+ // and A's type_idx must match that on the encoded_annotation (from the annotation_item).
+ // (Does not look through the subtypes recursively)
+ printDeclaredAnnotation(SingleUser.class, Calendar.class);
+ printDeclaredAnnotation(SingleUser.class, Calendars.class);
+
+ printDeclaredAnnotation(User.class, Calendar.class);
+ printDeclaredAnnotation(User.class, Calendars.class);
+
+ printDeclaredAnnotation(UserComplex.class, Calendar.class);
+ printDeclaredAnnotation(UserComplex.class, Calendars.class);
+
+ printDeclaredAnnotation(UserSub.class, Calendar.class);
+ printDeclaredAnnotation(UserSub.class, Calendars.class);
+
+ printDeclaredAnnotation(UserSub2.class, Calendar.class);
+ printDeclaredAnnotation(UserSub2.class, Calendars.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ public static void testDeclaredAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Declared class annotations by type:");
+ System.out.println("==============================");
+
+ // A is directly present or repeatably present on an element E;
+ // -- (does not do any recursion for classes regardless of @Inherited)
+ printDeclaredAnnotationsByType(Calendar.class, SingleUser.class);
+ printDeclaredAnnotationsByType(Calendars.class, SingleUser.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, User.class);
+ printDeclaredAnnotationsByType(Calendars.class, User.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, User2.class); // Enforce ordering 'z,x,y'
+ printDeclaredAnnotationsByType(Calendars.class, User2.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserComplex.class);
+ printDeclaredAnnotationsByType(Calendars.class, UserComplex.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserSub.class);
+ printDeclaredAnnotationsByType(Calendars.class, UserSub.class);
+
+ printDeclaredAnnotationsByType(Calendar.class, UserSub2.class);
+ // The directly present "Calendar" annotation masks all the repeatably present "Calendar"
+ // annotations coming from User.
+ printDeclaredAnnotationsByType(Calendars.class, UserSub2.class);
+ // Edge case: UserSub2 doesn't directly have a Calendars annotation,
+ // so it doesn't mask the "User" Calendars annotation.
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // "annotationUseClass."
+ private static <A extends Annotation> void printAnnotationsByType(Class<A> annotationClass,
+ Class<?> annotationUseClass) {
+ A[] annotationsByType = annotationUseClass.getAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by class "
+ + annotationUseClass.getName() + " with annotation " + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+
+ System.out.println(msg);
+ }
+
+ private static <A extends Annotation> void printDeclaredAnnotation(Class<?> annotationUseClass,
+ Class<A> annotationDefClass) {
+ A anno = annotationUseClass.getDeclaredAnnotation(annotationDefClass);
+
+ String msg = asString(anno);
+
+ System.out.println("Declared annotations by class " + annotationUseClass
+ + ", annotation " + annotationDefClass + ": " + msg);
+ }
+
+ // Print the annotation "annotationClass" that is directly/indirectly present with an element
+ // denoted by "annotationUseClass."
+ private static <A extends Annotation> void printDeclaredAnnotationsByType(
+ Class<A> annotationClass, Class<?> annotationUseClass) {
+ A[] annotationsByType = annotationUseClass.getDeclaredAnnotationsByType(annotationClass);
+
+ String msg = "Declared annnotations by type, defined by class " + annotationUseClass.getName()
+ + " with annotation " + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Method annotations by type:");
+ System.out.println("==============================");
+
+ // Print associated annotations:
+ // * A is directly present or repeatably present on an element E;
+ // * No annotation of A is directly/repeatably present on an element AND E is a class
+ // AND A's type is inheritable, AND A is associated with its superclass.
+ // (Looks through subtypes recursively only if there's 0 result at each level,
+ // and the annotation is @Inheritable).
+ printMethodAnnotationsByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodAnnotationsByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodAnnotationsByType(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // "annotationUseClass" method methodName.
+ private static <A extends Annotation> void printMethodAnnotationsByType(Class<A> annotationClass,
+ String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ A[] annotationsByType = m.getAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by method " + m.getName() + " with annotation " +
+ annotationClass.getName() + ": " +
+ asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodDeclaredAnnotations() {
+ System.out.println("==============================");
+ System.out.println("Declared method annotations:");
+ System.out.println("==============================");
+
+ printMethodDeclaredAnnotation(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotation(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotation(Calendars.class, "userComplex", AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // methodName in annotationUseClass.
+ private static <A extends Annotation> void printMethodDeclaredAnnotation(Class<A> annotationClass,
+ String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ Annotation annotationsByType = m.getDeclaredAnnotation(annotationClass);
+
+ String msg = "Annotations declared by method " + m.getName() + " with annotation "
+ + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+
+ public static void testMethodDeclaredAnnotationsByType() {
+ System.out.println("==============================");
+ System.out.println("Declared method annotations by type:");
+ System.out.println("==============================");
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "singleUser", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "singleUser", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "user", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "user", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "user2", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "user2", AnnotationTestFixture.class);
+
+ printMethodDeclaredAnnotationByType(Calendar.class, "userComplex", AnnotationTestFixture.class);
+ printMethodDeclaredAnnotationByType(Calendars.class, "userComplex",
+ AnnotationTestFixture.class);
+
+ System.out.println("-----------------------------");
+ System.out.println("-----------------------------");
+ }
+
+ // Print the annotation "annotationClass" that is associated with an element denoted by
+ // methodName in annotationUseClass.
+ private static <A extends Annotation> void printMethodDeclaredAnnotationByType(
+ Class<A> annotationClass, String methodName, Class<?> annotationUseClass) {
+ Method m = null;
+ try {
+ m = annotationUseClass.getDeclaredMethod(methodName);
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ }
+ A[] annotationsByType = m.getDeclaredAnnotationsByType(annotationClass);
+
+ String msg = "Annotations by type, defined by method " + m.getName() + " with annotation "
+ + annotationClass.getName() + ": "
+ + asString(annotationsByType);
+
+ System.out.println(msg);
+ }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestFixture.java b/test/048-reflect-v8/src/AnnotationTestFixture.java
new file mode 100644
index 0000000..248dfac
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestFixture.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class AnnotationTestFixture {
+
+ @Calendar(dayOfWeek="single", hour=23)
+ public static void singleUser() {
+
+ }
+ @Calendars ({
+ @Calendar(dayOfMonth="last"),
+ @Calendar(dayOfWeek="Fri", hour=23)
+ })
+ public static void user() {
+
+ }
+
+ @Calendars ({
+ @Calendar(dayOfMonth="z"),
+ @Calendar(dayOfMonth="x"),
+ @Calendar(dayOfMonth="y")
+ })
+ public static void user2() {
+
+ }
+
+ @Calendar(dayOfMonth="afirst")
+ @Calendars ({
+ @Calendar(dayOfMonth="zsecond"),
+ @Calendar(dayOfMonth="athird", hour=23)
+ })
+ public static void userComplex() {
+
+ }
+}
diff --git a/test/048-reflect-v8/src/AnnotationTestHelpers.java b/test/048-reflect-v8/src/AnnotationTestHelpers.java
new file mode 100644
index 0000000..6b5bea2
--- /dev/null
+++ b/test/048-reflect-v8/src/AnnotationTestHelpers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Annotation;
+
+public class AnnotationTestHelpers {
+ // Provide custom print function that print a deterministic output.
+ // Note that Annotation#toString has unspecified order: it prints out the
+ // fields, which is why we can't rely on it.
+
+ public static String asString(Annotation anno) {
+ if (anno instanceof Calendar) {
+ return asString((Calendar)anno);
+ } else if (anno instanceof Calendars) {
+ return asString((Calendars)anno);
+ } else {
+ if (anno == null) {
+ return "<null>";
+ }
+ // Fall-back, usually would only go here in a test failure.
+ return anno.toString();
+ }
+ }
+
+ public static String asString(Annotation[] annos) {
+ String msg = "";
+
+ if (annos == null) {
+ msg += "<null>";
+ } else if (annos.length == 0) {
+ msg += "<empty>";
+ } else {
+ for (int i = 0; i < annos.length; ++i) {
+ msg += asString(annos[i]);
+
+ if (i != annos.length - 1) {
+ msg += ", ";
+ }
+ }
+ }
+
+ return msg;
+ }
+
+ public static String asString(Calendar calendar) {
+ if (calendar == null) {
+ return "<null>";
+ }
+
+ return "@Calendar(dayOfMonth=" + calendar.dayOfMonth() + ", dayOfWeek=" +
+ calendar.dayOfWeek() + ", hour=" + calendar.hour() + ")";
+ }
+
+ public static String asString(Calendars calendars) {
+ if (calendars == null) {
+ return "<null>";
+ }
+
+ String s = "@Calendars(value=[";
+
+ Calendar[] allValues = calendars.value();
+ for (int i = 0; i < allValues.length; ++i) {
+ s += asString(allValues[i]);
+ if (i != allValues.length - 1) {
+ s += ", ";
+ }
+ }
+
+ s += "])";
+
+ return s;
+ }
+}
diff --git a/test/048-reflect-v8/src/Calendar.java b/test/048-reflect-v8/src/Calendar.java
new file mode 100644
index 0000000..4a16573
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendar.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// This is a plain old non-1.8 annotation. At runtime we can see that it has a
+// "Repeatable" annotation if we query with getDeclaredAnnotation(Repeatable.class)
+@Retention(RetentionPolicy.RUNTIME)
+@Repeatable(Calendars.class)
+@Inherited // note: container must also be @Inherited by JLS.
+public @interface Calendar {
+ String dayOfMonth() default "unspecified_month";
+ String dayOfWeek() default "unspecified_week";
+ int hour() default 6;
+}
+
diff --git a/test/048-reflect-v8/src/Calendars.java b/test/048-reflect-v8/src/Calendars.java
new file mode 100644
index 0000000..caeda52
--- /dev/null
+++ b/test/048-reflect-v8/src/Calendars.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+// Plain old annotation, there's nothing 1.8 specific about it.
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited // note: elements must also be @Inherited by JLS.
+public @interface Calendars {
+ Calendar[] value();
+}
diff --git a/test/048-reflect-v8/src/IFaceA.java b/test/048-reflect-v8/src/IFaceA.java
new file mode 100644
index 0000000..9b1f610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceA.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+@Calendars ({
+ @Calendar(dayOfMonth="if_a_first"),
+ @Calendar(dayOfMonth="if_b_last")
+})
+public interface IFaceA {
+}
diff --git a/test/048-reflect-v8/src/IFaceSimple.java b/test/048-reflect-v8/src/IFaceSimple.java
new file mode 100644
index 0000000..93cf610
--- /dev/null
+++ b/test/048-reflect-v8/src/IFaceSimple.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Simple annotation, no container.
+@Calendar(dayOfMonth="if_simple_first")
+public interface IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/IsDefaultTest.java b/test/048-reflect-v8/src/IsDefaultTest.java
new file mode 100644
index 0000000..177dcf1
--- /dev/null
+++ b/test/048-reflect-v8/src/IsDefaultTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class IsDefaultTest {
+ interface DefaultInterface {
+ default void sayHi() {
+ System.out.println("hi default");
+ }
+ }
+
+ interface RegularInterface {
+ void sayHi();
+ }
+
+ class ImplementsWithDefault implements DefaultInterface {}
+ class ImplementsWithRegular implements RegularInterface {
+ public void sayHi() {
+ System.out.println("hello specific");
+ }
+ }
+
+ private static void printIsDefault(Class<?> klass) {
+ Method m;
+ try {
+ m = klass.getMethod("sayHi");
+ } catch (Throwable t) {
+ System.out.println(t);
+ return;
+ }
+
+ boolean isDefault = m.isDefault();
+ System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
+ }
+
+ public static void test() {
+ System.out.println("==============================");
+ System.out.println("Are These Methods Default:");
+ System.out.println("==============================");
+
+ printIsDefault(DefaultInterface.class);
+ printIsDefault(RegularInterface.class);
+ printIsDefault(ImplementsWithDefault.class);
+ printIsDefault(ImplementsWithRegular.class);
+ }
+}
diff --git a/test/048-reflect-v8/src/Main.java b/test/048-reflect-v8/src/Main.java
index 7fa2a92..f2b8287 100644
--- a/test/048-reflect-v8/src/Main.java
+++ b/test/048-reflect-v8/src/Main.java
@@ -14,43 +14,14 @@
* limitations under the License.
*/
-import java.lang.reflect.Method;
-
public class Main {
- interface DefaultInterface {
- default void sayHi() {
- System.out.println("hi default");
- }
- }
-
- interface RegularInterface {
- void sayHi();
- }
-
- class ImplementsWithDefault implements DefaultInterface {}
- class ImplementsWithRegular implements RegularInterface {
- public void sayHi() {
- System.out.println("hello specific");
- }
- }
-
- private static void printIsDefault(Class<?> klass) {
- Method m;
- try {
- m = klass.getMethod("sayHi");
- } catch (Throwable t) {
- System.out.println(t);
- return;
- }
-
- boolean isDefault = m.isDefault();
- System.out.println(klass.getName() + " is default = " + (isDefault ? "yes" : "no"));
- }
-
public static void main(String[] args) {
- printIsDefault(DefaultInterface.class);
- printIsDefault(RegularInterface.class);
- printIsDefault(ImplementsWithDefault.class);
- printIsDefault(ImplementsWithRegular.class);
+ IsDefaultTest.test();
+ AnnotationTest.testAnnotationsByType();
+ AnnotationTest.testDeclaredAnnotation();
+ AnnotationTest.testDeclaredAnnotationsByType();
+ AnnotationTest.testMethodAnnotationsByType();
+ AnnotationTest.testMethodDeclaredAnnotations();
+ AnnotationTest.testMethodDeclaredAnnotationsByType();
}
}
diff --git a/test/048-reflect-v8/src/SingleUser.java b/test/048-reflect-v8/src/SingleUser.java
new file mode 100644
index 0000000..0f9c430
--- /dev/null
+++ b/test/048-reflect-v8/src/SingleUser.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a single "Calendar" annotation in the binary.
+@Calendar(dayOfWeek="single", hour=23)
+public class SingleUser {
+
+}
diff --git a/test/048-reflect-v8/src/User.java b/test/048-reflect-v8/src/User.java
new file mode 100644
index 0000000..003ceeb
--- /dev/null
+++ b/test/048-reflect-v8/src/User.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// in the binary.
+//
+/* FIXME: Use this code instead, when Jack supports repeatable annotations properly.
+ *
+ * @Calendar(dayOfMonth="last")
+ * @Calendar(dayOfWeek="Fri", hour=23)
+ */
+@Calendars ({
+ @Calendar(dayOfMonth="last"),
+ @Calendar(dayOfWeek="Fri", hour=23)
+})
+public class User {
+
+}
diff --git a/test/048-reflect-v8/src/User2.java b/test/048-reflect-v8/src/User2.java
new file mode 100644
index 0000000..1a6049f
--- /dev/null
+++ b/test/048-reflect-v8/src/User2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar,Calendar)
+// in the binary.
+// (Check for order, should be z,x,y)
+@Calendars ({
+ @Calendar(dayOfMonth="z"),
+ @Calendar(dayOfMonth="x"),
+ @Calendar(dayOfMonth="y")
+})
+public class User2 {
+
+}
diff --git a/test/048-reflect-v8/src/UserComplex.java b/test/048-reflect-v8/src/UserComplex.java
new file mode 100644
index 0000000..e262349
--- /dev/null
+++ b/test/048-reflect-v8/src/UserComplex.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Stored as a complex annotation Calendars(Calendar,Calendar)
+// followed by a Calendar in the binary.
+// In other words { Calendars([C,C]), C }
+//
+// Note that trying to do {C,Calendars,C} or similar
+// is illegal by the JLS.
+@Calendar(dayOfMonth="afirst")
+@Calendars ({
+ @Calendar(dayOfMonth="zsecond"),
+ @Calendar(dayOfMonth="athird", hour=23)
+})
+// @Calendar(dayOfMonth="zlast") // Leave for future ordering test
+public class UserComplex {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub.java b/test/048-reflect-v8/src/UserSub.java
new file mode 100644
index 0000000..d60aa6a
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class UserSub
+ extends User
+ implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/048-reflect-v8/src/UserSub2.java b/test/048-reflect-v8/src/UserSub2.java
new file mode 100644
index 0000000..13e2eb0
--- /dev/null
+++ b/test/048-reflect-v8/src/UserSub2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This calendar subsumes anything else we would've normally gotten from the subclass.
+@Calendar(dayOfMonth="sub2")
+public class UserSub2
+ extends User
+ implements IFaceA, IFaceSimple {
+
+}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 77301d2..87656bc 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -101,7 +101,12 @@
}
#endif
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(JNIEnv*, jobject, jint, jboolean) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
+ JNIEnv*,
+ jobject,
+ jboolean full_signatrues,
+ jint,
+ jboolean) {
#if __linux__
if (IsPicImage()) {
LOG(INFO) << "Image is pic, in-process unwinding check bypassed.";
@@ -122,14 +127,21 @@
// We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
// This is also risky, as deduping might play a trick on us, so the test needs to make sure that
// only unique functions are being expected.
+ // "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
"Java_Main_unwindInProcess", // This function.
- "boolean Main.unwindInProcess(int, boolean)", // The corresponding Java native method frame.
+ "Main.unwindInProcess", // The corresponding Java native method frame.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "Main.main" // The Java entry method.
+ };
+ std::vector<std::string> full_seq = {
+ "Java_Main_unwindInProcess", // This function.
+ "boolean Main.unwindInProcess(boolean, int, boolean)", // The corresponding Java native method frame.
"int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
"void Main.main(java.lang.String[])" // The Java entry method.
};
- bool result = CheckStack(bt.get(), seq);
+ bool result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
if (!kCauseSegfault) {
return result ? JNI_TRUE : JNI_FALSE;
} else {
@@ -178,7 +190,11 @@
}
#endif
-extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(JNIEnv*, jobject, jint pid_int) {
+extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindOtherProcess(
+ JNIEnv*,
+ jobject,
+ jboolean full_signatrues,
+ jint pid_int) {
#if __linux__
// TODO: What to do on Valgrind?
pid_t pid = static_cast<pid_t>(pid_int);
@@ -214,17 +230,27 @@
if (result) {
// See comment in unwindInProcess for non-exact stack matching.
+ // "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
// "Java_Main_sleep", // The sleep function being executed in the
// other runtime.
// Note: For some reason, the name isn't
// resolved, so don't look for it right now.
+ "Main.sleep", // The corresponding Java native method frame.
+ "int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
+ "Main.main" // The Java entry method.
+ };
+ std::vector<std::string> full_seq = {
+ // "Java_Main_sleep", // The sleep function being executed in the
+ // other runtime.
+ // Note: For some reason, the name isn't
+ // resolved, so don't look for it right now.
"boolean Main.sleep(int, boolean, double)", // The corresponding Java native method frame.
"int java.util.Arrays.binarySearch(java.lang.Object[], int, int, java.lang.Object, java.util.Comparator)", // Framework method.
"void Main.main(java.lang.String[])" // The Java entry method.
};
- result = CheckStack(bt.get(), seq);
+ result = CheckStack(bt.get(), full_signatrues ? full_seq : seq);
}
if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
diff --git a/test/137-cfi/expected.txt b/test/137-cfi/expected.txt
index 6a5618e..8db7853 100644
--- a/test/137-cfi/expected.txt
+++ b/test/137-cfi/expected.txt
@@ -1 +1,2 @@
JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/137-cfi/run b/test/137-cfi/run
index 9c567b6..6f4bcfe 100755
--- a/test/137-cfi/run
+++ b/test/137-cfi/run
@@ -14,4 +14,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-exec ${RUN} "$@" -Xcompiler-option --generate-debug-info
+# Test with full DWARF debugging information.
+# Check full signatures of methods.
+${RUN} "$@" -Xcompiler-option --generate-debug-info --args --full-signatures
+
+# Test with minimal compressed debugging information.
+# Check only method names (parameters are omitted to save space).
+${RUN} "$@" -Xcompiler-option --generate-mini-debug-info
diff --git a/test/137-cfi/src/Main.java b/test/137-cfi/src/Main.java
index 5474c9b..7755338 100644
--- a/test/137-cfi/src/Main.java
+++ b/test/137-cfi/src/Main.java
@@ -34,19 +34,28 @@
private boolean secondary;
+ private boolean full_signatures;
+
private boolean passed;
- public Main(boolean secondary) {
+ public Main(boolean secondary, boolean full_signatures) {
this.secondary = secondary;
+ this.full_signatures = full_signatures;
}
public static void main(String[] args) throws Exception {
System.loadLibrary(args[0]);
boolean secondary = false;
- if (args.length > 0 && args[args.length - 1].equals("--secondary")) {
+ boolean full_signatures = false;
+ for (String arg : args) {
+ if (arg.equals("--secondary")) {
secondary = true;
+ }
+ if (arg.equals("--full-signatures")) {
+ full_signatures = true;
+ }
}
- new Main(secondary).run();
+ new Main(secondary, full_signatures).run();
}
private void run() {
@@ -96,7 +105,7 @@
throw new RuntimeException(e);
}
- if (!unwindOtherProcess(pid)) {
+ if (!unwindOtherProcess(full_signatures, pid)) {
System.out.println("Unwinding other process failed.");
}
} finally {
@@ -154,7 +163,7 @@
if (b) {
return sleep(2, b, 1.0);
} else {
- return unwindInProcess(1, b);
+ return unwindInProcess(full_signatures, 1, b);
}
}
@@ -162,6 +171,6 @@
public native boolean sleep(int i, boolean b, double dummy);
- public native boolean unwindInProcess(int i, boolean b);
- public native boolean unwindOtherProcess(int pid);
+ public native boolean unwindInProcess(boolean full_signatures, int i, boolean b);
+ public native boolean unwindOtherProcess(boolean full_signatures, int pid);
}
diff --git a/test/564-checker-bitcount/src/Main.java b/test/564-checker-bitcount/src/Main.java
index b250145..2683b25 100644
--- a/test/564-checker-bitcount/src/Main.java
+++ b/test/564-checker-bitcount/src/Main.java
@@ -16,24 +16,20 @@
public class Main {
- // TODO: make this work when b/26700769 is done.
- //
+ // TODO: make something like this work when b/26700769 is done.
// CHECK-START-X86_64: int Main.bits32(int) disassembly (after)
// CHECK-DAG: popcnt
- //
- // CHECK-START-X86_64: int Main.bits32(int) disassembly (after)
- // CHECK-NOT: call
+
+ /// CHECK-START: int Main.bits32(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
+ /// CHECK-DAG: Return [<<Result>>]
private static int bits32(int x) {
return Integer.bitCount(x);
}
- // TODO: make this work when b/26700769 is done.
- //
- // CHECK-START-X86_64: int Main.bits64(long) disassembly (after)
- // CHECK-DAG: popcnt
- //
- // CHECK-START-X86_64: int Main.bits64(long) disassembly (after)
- // CHECK-NOT: call
+ /// CHECK-START: int Main.bits64(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongBitCount
+ /// CHECK-DAG: Return [<<Result>>]
private static int bits64(long x) {
return Long.bitCount(x);
}
diff --git a/test/565-checker-irreducible-loop/expected.txt b/test/565-checker-irreducible-loop/expected.txt
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/test/565-checker-irreducible-loop/expected.txt
@@ -0,0 +1,2 @@
+1
+1
diff --git a/test/565-checker-irreducible-loop/info.txt b/test/565-checker-irreducible-loop/info.txt
new file mode 100644
index 0000000..1e0dd02
--- /dev/null
+++ b/test/565-checker-irreducible-loop/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing in the presence of
+an irreducible loop.
diff --git a/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..29547ca
--- /dev/null
+++ b/test/565-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -0,0 +1,101 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Check that both the irreducible loop and the other loop entry
+# move the constant-folded value to where it's expected.
+
+## CHECK-START-X86: int IrreducibleLoop.test1(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test1(IJ)I
+ .registers 10
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ # In that block, v4 has no live range.
+ :other_loop_pre_entry
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+# Check that the compiler does not crash when
+# a live interval is found while connecting siblings, but that
+# live interval is inactive at the desired position.
+
+## CHECK-START-X86: int IrreducibleLoop.test2(int, long) register (after)
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:none
+## CHECK-DAG: ParallelMove {{.*84->.*}} loop:{{B\d+}} irreducible:true
+.method public static test2(IJ)I
+ .registers 14
+ const/16 v6, 2
+ const/16 v4, 1
+ const-wide/16 v0, 42
+ const-wide/16 v8, 68
+ add-long v2, v0, v0
+
+ if-eqz p0, :loop_entry
+ goto :other_loop_pre_entry
+
+ # The then part: beginning of the irreducible loop.
+ :loop_entry
+ if-eqz p0, :exit
+ cmp-long v6, v2, p1
+ :other_loop_entry
+ sub-int p0, p0, v4
+ goto :loop_entry
+
+ # The other block branching to the irreducible loop.
+ :other_loop_pre_entry
+ # Make v2 have a register location.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ # Stress register allocator on x86 to split v2.
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ if-eqz p0, :join
+ # Stress register allocator on x86 to split v2.
+ sput-wide p1, LIrreducibleLoop;->myField:J
+ sput-wide v8, LIrreducibleLoop;->myField:J
+ sput-wide v0, LIrreducibleLoop;->myField:J
+ # Last use of v2 before the irreducible loop, that
+ # will create an interval hole.
+ sput-wide v2, LIrreducibleLoop;->myField:J
+ :join
+ goto :other_loop_entry
+
+ :exit
+ return v6
+.end method
+
+.field public static volatile myField:J
diff --git a/test/565-checker-irreducible-loop/src/Main.java b/test/565-checker-irreducible-loop/src/Main.java
new file mode 100644
index 0000000..e48bd6b
--- /dev/null
+++ b/test/565-checker-irreducible-loop/src/Main.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ // Workaround for b/18051191.
+ class InnerClass {}
+
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ {
+ Method m = c.getMethod("test1", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+
+ {
+ Method m = c.getMethod("test2", int.class, long.class);
+ Object[] arguments = { 42, 31L };
+ System.out.println(m.invoke(null, arguments));
+ }
+ }
+}
diff --git a/test/565-checker-rotate/expected.txt b/test/565-checker-rotate/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/565-checker-rotate/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/565-checker-rotate/info.txt b/test/565-checker-rotate/info.txt
new file mode 100644
index 0000000..c6a8091
--- /dev/null
+++ b/test/565-checker-rotate/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit rotate operations.
diff --git a/test/565-checker-rotate/src/Main.java b/test/565-checker-rotate/src/Main.java
new file mode 100644
index 0000000..33bbe02
--- /dev/null
+++ b/test/565-checker-rotate/src/Main.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: int Main.rotateLeft32(int, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerRotateLeft
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int rotateLeft32(int x, int y) {
+ return Integer.rotateLeft(x, y);
+ }
+
+ /// CHECK-START: long Main.rotateLeft64(long, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongRotateLeft
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long rotateLeft64(long x, int y) {
+ return Long.rotateLeft(x, y);
+ }
+
+ /// CHECK-START: int Main.rotateRight32(int, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerRotateRight
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int rotateRight32(int x, int y) {
+ return Integer.rotateRight(x, y);
+ }
+
+ /// CHECK-START: long Main.rotateRight64(long, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongRotateRight
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long rotateRight64(long x, int y) {
+ return Long.rotateRight(x, y);
+ }
+
+ public static void main(String args[]) {
+ expectEquals32(0x00000001, rotateLeft32(0x00000001, 0));
+ expectEquals32(0x00000002, rotateLeft32(0x00000001, 1));
+ expectEquals32(0x80000000, rotateLeft32(0x00000001, 31));
+ expectEquals32(0x00000001, rotateLeft32(0x00000001, 32)); // overshoot
+ expectEquals32(0x00000003, rotateLeft32(0x80000001, 1));
+ expectEquals32(0x00000006, rotateLeft32(0x80000001, 2));
+ expectEquals32(0x23456781, rotateLeft32(0x12345678, 4));
+ expectEquals32(0xBCDEF09A, rotateLeft32(0x9ABCDEF0, 8));
+ for (int i = 0; i < 40; i++) { // overshoot a bit
+ int j = i & 31;
+ expectEquals32(0x00000000, rotateLeft32(0x00000000, i));
+ expectEquals32(0xFFFFFFFF, rotateLeft32(0xFFFFFFFF, i));
+ expectEquals32(1 << j, rotateLeft32(0x00000001, i));
+ expectEquals32((0x12345678 << j) | (0x12345678 >>> -j),
+ rotateLeft32(0x12345678, i));
+ }
+
+ expectEquals64(0x0000000000000001L, rotateLeft64(0x0000000000000001L, 0));
+ expectEquals64(0x0000000000000002L, rotateLeft64(0x0000000000000001L, 1));
+ expectEquals64(0x8000000000000000L, rotateLeft64(0x0000000000000001L, 63));
+ expectEquals64(0x0000000000000001L, rotateLeft64(0x0000000000000001L, 64)); // overshoot
+ expectEquals64(0x0000000000000003L, rotateLeft64(0x8000000000000001L, 1));
+ expectEquals64(0x0000000000000006L, rotateLeft64(0x8000000000000001L, 2));
+ expectEquals64(0x23456789ABCDEF01L, rotateLeft64(0x123456789ABCDEF0L, 4));
+ expectEquals64(0x3456789ABCDEF012L, rotateLeft64(0x123456789ABCDEF0L, 8));
+ for (int i = 0; i < 70; i++) { // overshoot a bit
+ int j = i & 63;
+ expectEquals64(0x0000000000000000L, rotateLeft64(0x0000000000000000L, i));
+ expectEquals64(0xFFFFFFFFFFFFFFFFL, rotateLeft64(0xFFFFFFFFFFFFFFFFL, i));
+ expectEquals64(1L << j, rotateLeft64(0x0000000000000001, i));
+ expectEquals64((0x123456789ABCDEF0L << j) | (0x123456789ABCDEF0L >>> -j),
+ rotateLeft64(0x123456789ABCDEF0L, i));
+ }
+
+ expectEquals32(0x80000000, rotateRight32(0x80000000, 0));
+ expectEquals32(0x40000000, rotateRight32(0x80000000, 1));
+ expectEquals32(0x00000001, rotateRight32(0x80000000, 31));
+ expectEquals32(0x80000000, rotateRight32(0x80000000, 32)); // overshoot
+ expectEquals32(0xC0000000, rotateRight32(0x80000001, 1));
+ expectEquals32(0x60000000, rotateRight32(0x80000001, 2));
+ expectEquals32(0x81234567, rotateRight32(0x12345678, 4));
+ expectEquals32(0xF09ABCDE, rotateRight32(0x9ABCDEF0, 8));
+ for (int i = 0; i < 40; i++) { // overshoot a bit
+ int j = i & 31;
+ expectEquals32(0x00000000, rotateRight32(0x00000000, i));
+ expectEquals32(0xFFFFFFFF, rotateRight32(0xFFFFFFFF, i));
+ expectEquals32(0x80000000 >>> j, rotateRight32(0x80000000, i));
+ expectEquals32((0x12345678 >>> j) | (0x12345678 << -j),
+ rotateRight32(0x12345678, i));
+ }
+
+ expectEquals64(0x8000000000000000L, rotateRight64(0x8000000000000000L, 0));
+ expectEquals64(0x4000000000000000L, rotateRight64(0x8000000000000000L, 1));
+ expectEquals64(0x0000000000000001L, rotateRight64(0x8000000000000000L, 63));
+ expectEquals64(0x8000000000000000L, rotateRight64(0x8000000000000000L, 64)); // overshoot
+ expectEquals64(0xC000000000000000L, rotateRight64(0x8000000000000001L, 1));
+ expectEquals64(0x6000000000000000L, rotateRight64(0x8000000000000001L, 2));
+ expectEquals64(0x0123456789ABCDEFL, rotateRight64(0x123456789ABCDEF0L, 4));
+ expectEquals64(0xF0123456789ABCDEL, rotateRight64(0x123456789ABCDEF0L, 8));
+ for (int i = 0; i < 70; i++) { // overshoot a bit
+ int j = i & 63;
+ expectEquals64(0x0000000000000000L, rotateRight64(0x0000000000000000L, i));
+ expectEquals64(0xFFFFFFFFFFFFFFFFL, rotateRight64(0xFFFFFFFFFFFFFFFFL, i));
+ expectEquals64(0x8000000000000000L >>> j, rotateRight64(0x8000000000000000L, i));
+ expectEquals64((0x123456789ABCDEF0L >>> j) | (0x123456789ABCDEF0L << -j),
+ rotateRight64(0x123456789ABCDEF0L, i));
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals32(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+ private static void expectEquals64(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/566-checker-signum/expected.txt b/test/566-checker-signum/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/566-checker-signum/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/566-checker-signum/info.txt b/test/566-checker-signum/info.txt
new file mode 100644
index 0000000..328e494
--- /dev/null
+++ b/test/566-checker-signum/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit signum operations.
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
new file mode 100644
index 0000000..cc4a984
--- /dev/null
+++ b/test/566-checker-signum/src/Main.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: int Main.sign32(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int sign32(int x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.sign64(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int sign64(long x) {
+ return Long.signum(x);
+ }
+
+ public static void main(String args[]) {
+ expectEquals(-1, sign32(Integer.MIN_VALUE));
+ expectEquals(-1, sign32(-12345));
+ expectEquals(-1, sign32(-1));
+ expectEquals(0, sign32(0));
+ expectEquals(1, sign32(1));
+ expectEquals(1, sign32(12345));
+ expectEquals(1, sign32(Integer.MAX_VALUE));
+
+ for (int i = -11; i <= 11; i++) {
+ int expected = 0;
+ if (i < 0) expected = -1;
+ else if (i > 0) expected = 1;
+ expectEquals(expected, sign32(i));
+ }
+
+ expectEquals(-1, sign64(Long.MIN_VALUE));
+ expectEquals(-1, sign64(-12345L));
+ expectEquals(-1, sign64(-1L));
+ expectEquals(0, sign64(0L));
+ expectEquals(1, sign64(1L));
+ expectEquals(1, sign64(12345L));
+ expectEquals(1, sign64(Long.MAX_VALUE));
+
+ for (long i = -11L; i <= 11L; i++) {
+ int expected = 0;
+ if (i < 0) expected = -1;
+ else if (i > 0) expected = 1;
+ expectEquals(expected, sign64(i));
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/567-checker-compare/expected.txt b/test/567-checker-compare/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/567-checker-compare/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/567-checker-compare/info.txt b/test/567-checker-compare/info.txt
new file mode 100644
index 0000000..5bac7b1
--- /dev/null
+++ b/test/567-checker-compare/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit compare operations.
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
new file mode 100644
index 0000000..52abb75
--- /dev/null
+++ b/test/567-checker-compare/src/Main.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: int Main.compare32(int, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int compare32(int x, int y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compare64(long, long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongCompare
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int compare64(long x, long y) {
+ return Long.compare(x, y);
+ }
+
+ public static void main(String args[]) {
+ expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE + 1));
+ expectEquals(-1, compare32(Integer.MIN_VALUE, -1));
+ expectEquals(-1, compare32(Integer.MIN_VALUE, 0));
+ expectEquals(-1, compare32(Integer.MIN_VALUE, 1));
+ expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(-1, compare32(-1, 0));
+ expectEquals(-1, compare32(-1, 1));
+ expectEquals(-1, compare32(0, 1));
+
+ expectEquals(0, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE));
+ expectEquals(0, compare32(-1, -1));
+ expectEquals(0, compare32(0, 0));
+ expectEquals(0, compare32(1, 1));
+ expectEquals(0, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+ expectEquals(1, compare32(0, -1));
+ expectEquals(1, compare32(1, -1));
+ expectEquals(1, compare32(1, 0));
+ expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compare32(Integer.MAX_VALUE, -1));
+ expectEquals(1, compare32(Integer.MAX_VALUE, 0));
+ expectEquals(1, compare32(Integer.MAX_VALUE, 1));
+ expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
+
+ for (int i = -11; i <= 11; i++) {
+ for (int j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compare32(i, j));
+ }
+ }
+
+ expectEquals(-1, compare64(Long.MIN_VALUE, Long.MIN_VALUE + 1L));
+ expectEquals(-1, compare64(Long.MIN_VALUE, -1L));
+ expectEquals(-1, compare64(Long.MIN_VALUE, 0L));
+ expectEquals(-1, compare64(Long.MIN_VALUE, 1L));
+ expectEquals(-1, compare64(Long.MIN_VALUE, Long.MAX_VALUE));
+ expectEquals(-1, compare64(-1L, 0L));
+ expectEquals(-1, compare64(-1L, 1L));
+ expectEquals(-1, compare64(0L, 1L));
+
+ expectEquals(0, compare64(Long.MIN_VALUE, Long.MIN_VALUE));
+ expectEquals(0, compare64(-1L, -1L));
+ expectEquals(0, compare64(0L, 0L));
+ expectEquals(0, compare64(1L, 1L));
+ expectEquals(0, compare64(Long.MAX_VALUE, Long.MAX_VALUE));
+
+ expectEquals(1, compare64(0L, -1L));
+ expectEquals(1, compare64(1L, -1L));
+ expectEquals(1, compare64(1L, 0L));
+ expectEquals(1, compare64(Long.MAX_VALUE, Long.MIN_VALUE));
+ expectEquals(1, compare64(Long.MAX_VALUE, -1L));
+ expectEquals(1, compare64(Long.MAX_VALUE, 0L));
+ expectEquals(1, compare64(Long.MAX_VALUE, 1L));
+ expectEquals(1, compare64(Long.MAX_VALUE, Long.MAX_VALUE - 1L));
+
+ for (long i = -11L; i <= 11L; i++) {
+ for (long j = -11L; j <= 11L; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compare64(i, j));
+ }
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/568-checker-onebit/expected.txt b/test/568-checker-onebit/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/568-checker-onebit/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/568-checker-onebit/info.txt b/test/568-checker-onebit/info.txt
new file mode 100644
index 0000000..c2b5bf8
--- /dev/null
+++ b/test/568-checker-onebit/info.txt
@@ -0,0 +1 @@
+Unit test for 32-bit and 64-bit high/low-bit operations.
diff --git a/test/568-checker-onebit/src/Main.java b/test/568-checker-onebit/src/Main.java
new file mode 100644
index 0000000..7007c6a
--- /dev/null
+++ b/test/568-checker-onebit/src/Main.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ /// CHECK-START: int Main.hi32(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerHighestOneBit
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int hi32(int x) {
+ return Integer.highestOneBit(x);
+ }
+
+ /// CHECK-START: int Main.lo32(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerLowestOneBit
+ /// CHECK-DAG: Return [<<Result>>]
+ private static int lo32(int x) {
+ return Integer.lowestOneBit(x);
+ }
+
+ /// CHECK-START: long Main.hi64(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongHighestOneBit
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long hi64(long x) {
+ return Long.highestOneBit(x);
+ }
+
+ /// CHECK-START: long Main.lo64(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:j\d+>> InvokeStaticOrDirect intrinsic:LongLowestOneBit
+ /// CHECK-DAG: Return [<<Result>>]
+ private static long lo64(long x) {
+ return Long.lowestOneBit(x);
+ }
+
+ public static void main(String args[]) {
+ expectEquals32(0x00000000, hi32(0x00000000));
+ expectEquals32(0x00000000, lo32(0x00000000));
+ expectEquals32(0x00010000, hi32(0x00010000));
+ expectEquals32(0x00010000, lo32(0x00010000));
+ expectEquals32(0x00800000, hi32(0x00FF0000));
+ expectEquals32(0x00010000, lo32(0x00FF0000));
+ expectEquals32(0x80000000, hi32(0xFFFFFFFF));
+ expectEquals32(0x00000001, lo32(0xFFFFFFFF));
+
+ for (int i = 0; i < 32; i++) {
+ expectEquals32(1 << i, hi32(1 << i));
+ expectEquals32(1 << i, lo32(1 << i));
+ int expected = i < 29 ? 0x8 << i : 0x80000000;
+ expectEquals32(expected, hi32(0xF << i));
+ expectEquals32(0x1 << i, lo32(0xF << i));
+ }
+
+ expectEquals64(0x0000000000000000L, hi64(0x0000000000000000L));
+ expectEquals64(0x0000000000000000L, lo64(0x0000000000000000L));
+ expectEquals64(0x0000000100000000L, hi64(0x0000000100000000L));
+ expectEquals64(0x0000000100000000L, lo64(0x0000000100000000L));
+ expectEquals64(0x0000008000000000L, hi64(0x000000FF00000000L));
+ expectEquals64(0x0000000100000000L, lo64(0x000000FF00000000L));
+ expectEquals64(0x8000000000000000L, hi64(0xFFFFFFFFFFFFFFFFL));
+ expectEquals64(0x0000000000000001L, lo64(0xFFFFFFFFFFFFFFFFL));
+
+ for (int i = 0; i < 64; i++) {
+ expectEquals64(1L << i, hi64(1L << i));
+ expectEquals64(1L << i, lo64(1L << i));
+ long expected = i < 61 ? 0x8L << i : 0x8000000000000000L;
+ expectEquals64(expected, hi64(0xFL << i));
+ expectEquals64(0x1L << i, lo64(0xFL << i));
+ }
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals32(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+ private static void expectEquals64(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index e004b6c..8245ccf 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -74,6 +74,14 @@
fi
ARGS="${ARGS} $1"
shift
+ elif [ "x$1" = "x--args" ]; then
+ shift
+ if [ "x$1" = "x" ]; then
+ echo "$0 missing argument to --args" 1>&2
+ exit 1
+ fi
+ ARGS="${ARGS} $1"
+ shift
elif [ "x$1" = "x-Xcompiler-option" ]; then
shift
option="$1"
@@ -533,6 +541,7 @@
cd $ANDROID_BUILD_TOP
+ rm -rf ${DEX_LOCATION}/dalvik-cache/
$mkdir_cmdline || exit 1
$dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; }
diff --git a/tools/ahat/README.txt b/tools/ahat/README.txt
index a3ecf86..da5225c 100644
--- a/tools/ahat/README.txt
+++ b/tools/ahat/README.txt
@@ -77,6 +77,9 @@
* Instance.isRoot and Instance.getRootTypes.
Release History:
+ 0.4 Pending
+ Show registered native allocations for heap dumps that support it.
+
0.3 Dec 15, 2015
Fix page loading performance by showing a limited number of entries by default.
Fix mismatch between overview and "roots" totals.
diff --git a/tools/ahat/src/AhatSnapshot.java b/tools/ahat/src/AhatSnapshot.java
index fc7911b..2adec6f 100644
--- a/tools/ahat/src/AhatSnapshot.java
+++ b/tools/ahat/src/AhatSnapshot.java
@@ -43,22 +43,27 @@
* ahat.
*/
class AhatSnapshot {
- private Snapshot mSnapshot;
- private List<Heap> mHeaps;
+ private final Snapshot mSnapshot;
+ private final List<Heap> mHeaps;
// Map from Instance to the list of Instances it immediately dominates.
- private Map<Instance, List<Instance>> mDominated;
+ private final Map<Instance, List<Instance>> mDominated
+ = new HashMap<Instance, List<Instance>>();
// Collection of objects whose immediate dominator is the SENTINEL_ROOT.
- private List<Instance> mRooted;
+ private final List<Instance> mRooted = new ArrayList<Instance>();
// Map from roots to their types.
// Instances are only included if they are roots, and the collection of root
// types is guaranteed to be non-empty.
- private Map<Instance, Collection<RootType>> mRoots;
+ private final Map<Instance, Collection<RootType>> mRoots
+ = new HashMap<Instance, Collection<RootType>>();
- private Site mRootSite;
- private Map<Heap, Long> mHeapSizes;
+ private final Site mRootSite = new Site("ROOT");
+ private final Map<Heap, Long> mHeapSizes = new HashMap<Heap, Long>();
+
+ private final List<InstanceUtils.NativeAllocation> mNativeAllocations
+ = new ArrayList<InstanceUtils.NativeAllocation>();
/**
* Create an AhatSnapshot from an hprof file.
@@ -77,10 +82,6 @@
private AhatSnapshot(Snapshot snapshot) {
mSnapshot = snapshot;
mHeaps = new ArrayList<Heap>(mSnapshot.getHeaps());
- mDominated = new HashMap<Instance, List<Instance>>();
- mRootSite = new Site("ROOT");
- mHeapSizes = new HashMap<Heap, Long>();
- mRooted = new ArrayList<Instance>();
ClassObj javaLangClass = mSnapshot.findClass("java.lang.Class");
for (Heap heap : mHeaps) {
@@ -118,13 +119,18 @@
}
}
mRootSite.add(stackId, 0, path.iterator(), inst);
+
+ // Update native allocations.
+ InstanceUtils.NativeAllocation alloc = InstanceUtils.getNativeAllocation(inst);
+ if (alloc != null) {
+ mNativeAllocations.add(alloc);
+ }
}
}
mHeapSizes.put(heap, total);
}
// Record the roots and their types.
- mRoots = new HashMap<Instance, Collection<RootType>>();
for (RootObj root : snapshot.getGCRoots()) {
Instance inst = root.getReferredInstance();
Collection<RootType> types = mRoots.get(inst);
@@ -259,4 +265,9 @@
}
return site;
}
+
+ // Return a list of known native allocations in the snapshot.
+ public List<InstanceUtils.NativeAllocation> getNativeAllocations() {
+ return mNativeAllocations;
+ }
}
diff --git a/tools/ahat/src/InstanceUtils.java b/tools/ahat/src/InstanceUtils.java
index 7fa53c7..8b7f9ea 100644
--- a/tools/ahat/src/InstanceUtils.java
+++ b/tools/ahat/src/InstanceUtils.java
@@ -20,6 +20,7 @@
import com.android.tools.perflib.heap.ClassInstance;
import com.android.tools.perflib.heap.ClassObj;
import com.android.tools.perflib.heap.Instance;
+import com.android.tools.perflib.heap.Heap;
import com.android.tools.perflib.heap.Type;
import java.awt.image.BufferedImage;
@@ -31,7 +32,7 @@
* Returns true if the given instance is an instance of a class with the
* given name.
*/
- public static boolean isInstanceOfClass(Instance inst, String className) {
+ private static boolean isInstanceOfClass(Instance inst, String className) {
ClassObj cls = (inst == null) ? null : inst.getClassObj();
return (cls != null && className.equals(cls.getClassName()));
}
@@ -118,12 +119,12 @@
return null;
}
- Integer width = getIntField(inst, "mWidth");
+ Integer width = getIntField(inst, "mWidth", null);
if (width == null) {
return null;
}
- Integer height = getIntField(inst, "mHeight");
+ Integer height = getIntField(inst, "mHeight", null);
if (height == null) {
return null;
}
@@ -186,23 +187,29 @@
/**
* Read an int field of an instance.
* The field is assumed to be an int type.
- * Returns null if the field value is not an int or could not be read.
+ * Returns <code>def</code> if the field value is not an int or could not be
+ * read.
*/
- private static Integer getIntField(Instance inst, String fieldName) {
+ private static Integer getIntField(Instance inst, String fieldName, Integer def) {
Object value = getField(inst, fieldName);
if (!(value instanceof Integer)) {
- return null;
+ return def;
}
return (Integer)value;
}
/**
- * Read an int field of an instance, returning a default value if the field
- * was not an int or could not be read.
+ * Read a long field of an instance.
+ * The field is assumed to be a long type.
+ * Returns <code>def</code> if the field value is not an long or could not
+ * be read.
*/
- private static int getIntField(Instance inst, String fieldName, int def) {
- Integer value = getIntField(inst, fieldName);
- return value == null ? def : value;
+ private static Long getLongField(Instance inst, String fieldName, Long def) {
+ Object value = getField(inst, fieldName);
+ if (!(value instanceof Long)) {
+ return def;
+ }
+ return (Long)value;
}
/**
@@ -278,4 +285,73 @@
}
return null;
}
+
+ public static class NativeAllocation {
+ public long size;
+ public Heap heap;
+ public long pointer;
+ public Instance referent;
+
+ public NativeAllocation(long size, Heap heap, long pointer, Instance referent) {
+ this.size = size;
+ this.heap = heap;
+ this.pointer = pointer;
+ this.referent = referent;
+ }
+ }
+
+ /**
+ * Assuming inst represents a NativeAllocation, return information about the
+ * native allocation. Returns null if the given instance doesn't represent a
+ * native allocation.
+ */
+ public static NativeAllocation getNativeAllocation(Instance inst) {
+ if (!isInstanceOfClass(inst, "libcore.util.NativeAllocationRegistry$CleanerThunk")) {
+ return null;
+ }
+
+ Long pointer = InstanceUtils.getLongField(inst, "nativePtr", null);
+ if (pointer == null) {
+ return null;
+ }
+
+ // Search for the registry field of inst.
+ // Note: We know inst as an instance of ClassInstance because we already
+ // read the nativePtr field from it.
+ Instance registry = null;
+ for (ClassInstance.FieldValue field : ((ClassInstance)inst).getValues()) {
+ Object fieldValue = field.getValue();
+ if (fieldValue instanceof Instance) {
+ Instance fieldInst = (Instance)fieldValue;
+ if (isInstanceOfClass(fieldInst, "libcore.util.NativeAllocationRegistry")) {
+ registry = fieldInst;
+ break;
+ }
+ }
+ }
+
+ if (registry == null) {
+ return null;
+ }
+
+ Long size = InstanceUtils.getLongField(registry, "size", null);
+ if (size == null) {
+ return null;
+ }
+
+ Instance referent = null;
+ for (Instance ref : inst.getHardReferences()) {
+ if (isInstanceOfClass(ref, "sun.misc.Cleaner")) {
+ referent = InstanceUtils.getReferent(ref);
+ if (referent != null) {
+ break;
+ }
+ }
+ }
+
+ if (referent == null) {
+ return null;
+ }
+ return new NativeAllocation(size, inst.getHeap(), pointer, referent);
+ }
}
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 091820f..d784599 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -78,6 +78,7 @@
server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
+ server.createContext("/native", new AhatHttpHandler(new NativeAllocationsHandler(ahat)));
server.createContext("/bitmap", new BitmapHandler(ahat));
server.createContext("/help", new HelpHandler());
server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
diff --git a/tools/ahat/src/Menu.java b/tools/ahat/src/Menu.java
index 018e019..232b849 100644
--- a/tools/ahat/src/Menu.java
+++ b/tools/ahat/src/Menu.java
@@ -27,6 +27,8 @@
.append(" - ")
.appendLink(DocString.uri("sites"), DocString.text("allocations"))
.append(" - ")
+ .appendLink(DocString.uri("native"), DocString.text("native"))
+ .append(" - ")
.appendLink(DocString.uri("help"), DocString.text("help"));
/**
diff --git a/tools/ahat/src/NativeAllocationsHandler.java b/tools/ahat/src/NativeAllocationsHandler.java
new file mode 100644
index 0000000..17407e1
--- /dev/null
+++ b/tools/ahat/src/NativeAllocationsHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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 java.io.IOException;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+class NativeAllocationsHandler implements AhatHandler {
+ private static final String ALLOCATIONS_ID = "allocations";
+
+ private AhatSnapshot mSnapshot;
+
+ public NativeAllocationsHandler(AhatSnapshot snapshot) {
+ mSnapshot = snapshot;
+ }
+
+ @Override
+ public void handle(Doc doc, Query query) throws IOException {
+ List<InstanceUtils.NativeAllocation> allocs = mSnapshot.getNativeAllocations();
+
+ doc.title("Registered Native Allocations");
+
+ doc.section("Overview");
+ long totalSize = 0;
+ for (InstanceUtils.NativeAllocation alloc : allocs) {
+ totalSize += alloc.size;
+ }
+ doc.descriptions();
+ doc.description(DocString.text("Number of Registered Native Allocations"),
+ DocString.format("%,14d", allocs.size()));
+ doc.description(DocString.text("Total Size of Registered Native Allocations"),
+ DocString.format("%,14d", totalSize));
+ doc.end();
+
+ doc.section("List of Allocations");
+ if (allocs.isEmpty()) {
+ doc.println(DocString.text("(none)"));
+ } else {
+ doc.table(
+ new Column("Size", Column.Align.RIGHT),
+ new Column("Heap"),
+ new Column("Native Pointer"),
+ new Column("Referent"));
+ Comparator<InstanceUtils.NativeAllocation> compare
+ = new Sort.WithPriority<InstanceUtils.NativeAllocation>(
+ new Sort.NativeAllocationByHeapName(),
+ new Sort.NativeAllocationBySize());
+ Collections.sort(allocs, compare);
+ SubsetSelector<InstanceUtils.NativeAllocation> selector
+ = new SubsetSelector(query, ALLOCATIONS_ID, allocs);
+ for (InstanceUtils.NativeAllocation alloc : selector.selected()) {
+ doc.row(
+ DocString.format("%,14d", alloc.size),
+ DocString.text(alloc.heap.getName()),
+ DocString.format("0x%x", alloc.pointer),
+ Value.render(mSnapshot, alloc.referent));
+ }
+
+ // Print a summary of the remaining entries if there are any.
+ List<InstanceUtils.NativeAllocation> remaining = selector.remaining();
+ if (!remaining.isEmpty()) {
+ long total = 0;
+ for (InstanceUtils.NativeAllocation alloc : remaining) {
+ total += alloc.size;
+ }
+
+ doc.row(
+ DocString.format("%,14d", total),
+ DocString.text("..."),
+ DocString.text("..."),
+ DocString.text("..."));
+ }
+
+ doc.end();
+ selector.render(doc);
+ }
+ }
+}
+
diff --git a/tools/ahat/src/OverviewHandler.java b/tools/ahat/src/OverviewHandler.java
index 720fcb4..0dbad7e 100644
--- a/tools/ahat/src/OverviewHandler.java
+++ b/tools/ahat/src/OverviewHandler.java
@@ -48,6 +48,22 @@
doc.section("Heap Sizes");
printHeapSizes(doc, query);
+
+ List<InstanceUtils.NativeAllocation> allocs = mSnapshot.getNativeAllocations();
+ if (!allocs.isEmpty()) {
+ doc.section("Registered Native Allocations");
+ long totalSize = 0;
+ for (InstanceUtils.NativeAllocation alloc : allocs) {
+ totalSize += alloc.size;
+ }
+ doc.descriptions();
+ doc.description(DocString.text("Number of Registered Native Allocations"),
+ DocString.format("%,14d", allocs.size()));
+ doc.description(DocString.text("Total Size of Registered Native Allocations"),
+ DocString.format("%,14d", totalSize));
+ doc.end();
+ }
+
doc.big(Menu.getMenu());
}
diff --git a/tools/ahat/src/Sort.java b/tools/ahat/src/Sort.java
index 3b79166..c5f89c3 100644
--- a/tools/ahat/src/Sort.java
+++ b/tools/ahat/src/Sort.java
@@ -177,5 +177,31 @@
return aName.compareTo(bName);
}
}
+
+ /**
+ * Compare AhatSnapshot.NativeAllocation by heap name.
+ * Different allocations with the same heap name are considered equal for
+ * the purposes of comparison.
+ */
+ public static class NativeAllocationByHeapName
+ implements Comparator<InstanceUtils.NativeAllocation> {
+ @Override
+ public int compare(InstanceUtils.NativeAllocation a, InstanceUtils.NativeAllocation b) {
+ return a.heap.getName().compareTo(b.heap.getName());
+ }
+ }
+
+ /**
+ * Compare InstanceUtils.NativeAllocation by their size.
+ * Different allocations with the same size are considered equal for the
+ * purposes of comparison.
+ * This sorts allocations from larger size to smaller size.
+ */
+ public static class NativeAllocationBySize implements Comparator<InstanceUtils.NativeAllocation> {
+ @Override
+ public int compare(InstanceUtils.NativeAllocation a, InstanceUtils.NativeAllocation b) {
+ return Long.compare(b.size, a.size);
+ }
+ }
}
diff --git a/tools/ahat/test-dump/Main.java b/tools/ahat/test-dump/Main.java
index 90cd7af..701d60e 100644
--- a/tools/ahat/test-dump/Main.java
+++ b/tools/ahat/test-dump/Main.java
@@ -19,6 +19,7 @@
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
+import libcore.util.NativeAllocationRegistry;
/**
* Program used to create a heap dump for test purposes.
@@ -47,6 +48,9 @@
for (int i = 0; i < N; i++) {
bigArray[i] = (byte)((i*i) & 0xFF);
}
+
+ NativeAllocationRegistry registry = new NativeAllocationRegistry(0x12345, 42);
+ registry.registerNativeAllocation(anObject, 0xABCDABCD);
}
}
diff --git a/tools/ahat/test/NativeAllocationTest.java b/tools/ahat/test/NativeAllocationTest.java
new file mode 100644
index 0000000..7ad4c1d
--- /dev/null
+++ b/tools/ahat/test/NativeAllocationTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ahat;
+
+import com.android.tools.perflib.heap.Instance;
+import java.io.IOException;
+import static org.junit.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class NativeAllocationTest {
+
+ @Test
+ public void nativeAllocation() throws IOException {
+ TestDump dump = TestDump.getTestDump();
+
+ AhatSnapshot snapshot = dump.getAhatSnapshot();
+ Instance referent = (Instance)dump.getDumpedThing("anObject");
+ for (InstanceUtils.NativeAllocation alloc : snapshot.getNativeAllocations()) {
+ if (alloc.referent == referent) {
+ assertEquals(42 , alloc.size);
+ assertEquals(referent.getHeap(), alloc.heap);
+ assertEquals(0xABCDABCD , alloc.pointer);
+ return;
+ }
+ }
+ fail("No native allocation found with anObject as the referent");
+ }
+}
+
diff --git a/tools/ahat/test/Tests.java b/tools/ahat/test/Tests.java
index e8894e2..3291470 100644
--- a/tools/ahat/test/Tests.java
+++ b/tools/ahat/test/Tests.java
@@ -23,6 +23,7 @@
if (args.length == 0) {
args = new String[]{
"com.android.ahat.InstanceUtilsTest",
+ "com.android.ahat.NativeAllocationTest",
"com.android.ahat.PerformanceTest",
"com.android.ahat.QueryTest",
"com.android.ahat.SortTest",