summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/common_compiler_test.cc6
-rw-r--r--compiler/common_compiler_test.h2
-rw-r--r--compiler/debug/dwarf/debug_line_opcode_writer.h14
-rw-r--r--compiler/debug/dwarf/dwarf_test.cc4
-rw-r--r--compiler/debug/elf_debug_line_writer.h9
-rw-r--r--compiler/debug/method_debug_info.h1
-rw-r--r--compiler/driver/compiler_options.h6
-rw-r--r--compiler/image_test.cc9
-rw-r--r--compiler/image_writer.cc36
-rw-r--r--compiler/jit/jit_compiler.cc7
-rw-r--r--compiler/oat_writer.cc4
-rw-r--r--compiler/optimizing/inliner.cc63
-rw-r--r--compiler/optimizing/instruction_simplifier.cc16
-rw-r--r--compiler/optimizing/intrinsics.h5
-rw-r--r--compiler/optimizing/intrinsics_arm.cc3
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc3
-rw-r--r--compiler/optimizing/intrinsics_mips.cc3
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc3
-rw-r--r--compiler/optimizing/intrinsics_x86.cc3
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc3
-rw-r--r--compiler/optimizing/nodes.h1
-rw-r--r--compiler/optimizing/optimizing_compiler.cc4
-rw-r--r--dex2oat/dex2oat.cc26
-rw-r--r--oatdump/oatdump.cc22
-rw-r--r--runtime/base/unix_file/fd_file.cc19
-rw-r--r--runtime/base/unix_file/fd_file.h4
-rw-r--r--runtime/base/unix_file/fd_file_test.cc28
-rw-r--r--runtime/class_linker.cc5
-rw-r--r--runtime/common_runtime_test.cc1
-rw-r--r--runtime/gc/heap.cc8
-rw-r--r--runtime/gc/space/image_space.cc63
-rw-r--r--runtime/jit/jit.cc6
-rw-r--r--runtime/jit/jit.h12
-rw-r--r--runtime/jit/jit_code_cache.cc29
-rw-r--r--runtime/jit/jit_code_cache.h10
-rw-r--r--runtime/jit/profile_saver.cc126
-rw-r--r--runtime/jit/profile_saver.h18
-rw-r--r--runtime/jit/profiling_info.cc4
-rw-r--r--runtime/jit/profiling_info.h30
-rw-r--r--runtime/mem_map.cc14
-rw-r--r--runtime/native/dalvik_system_DexFile.cc54
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc24
-rw-r--r--runtime/oat.cc20
-rw-r--r--runtime/oat.h8
-rw-r--r--runtime/oat_file.cc9
-rw-r--r--runtime/oat_file.h2
-rw-r--r--runtime/oat_file_assistant.cc208
-rw-r--r--runtime/oat_file_assistant.h117
-rw-r--r--runtime/oat_file_assistant_test.cc188
-rw-r--r--runtime/oat_file_manager.cc9
-rw-r--r--runtime/quick/inline_method_analyser.cc7
-rw-r--r--runtime/runtime.cc18
-rw-r--r--runtime/runtime.h5
-rw-r--r--runtime/runtime_linux.cc5
-rw-r--r--test/004-checker-UnsafeTest18/src/Main.java18
-rw-r--r--test/141-class-unload/src/Main.java1
-rw-r--r--test/145-alloc-tracking-stress/src/Main.java1
-rw-r--r--test/577-profile-foreign-dex/expected.txt0
-rw-r--r--test/577-profile-foreign-dex/info.txt1
-rw-r--r--test/577-profile-foreign-dex/run20
-rw-r--r--test/577-profile-foreign-dex/src-ex/OtherDex.java17
-rw-r--r--test/577-profile-foreign-dex/src/Main.java175
-rw-r--r--test/Android.run-test.mk4
63 files changed, 1021 insertions, 520 deletions
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index 239bc590e9..6075cd6fbe 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -187,7 +187,9 @@ void CommonCompilerTest::SetUp() {
}
}
-void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa) {
+void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind,
+ InstructionSet isa,
+ size_t number_of_threads) {
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
verification_results_.get(),
method_inliner_map_.get(),
@@ -198,7 +200,7 @@ void CommonCompilerTest::CreateCompilerDriver(Compiler::Kind kind, InstructionSe
GetImageClasses(),
GetCompiledClasses(),
GetCompiledMethods(),
- /* thread_count */ 2,
+ number_of_threads,
/* dump_stats */ true,
/* dump_passes */ true,
timer_.get(),
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 7e0fbabff8..9552143080 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -93,7 +93,7 @@ class CommonCompilerTest : public CommonRuntimeTest {
const char* method_name, const char* signature)
SHARED_REQUIRES(Locks::mutator_lock_);
- void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa);
+ void CreateCompilerDriver(Compiler::Kind kind, InstructionSet isa, size_t number_of_threads = 2U);
void ReserveImageSpace();
diff --git a/compiler/debug/dwarf/debug_line_opcode_writer.h b/compiler/debug/dwarf/debug_line_opcode_writer.h
index 58502a3f9c..b4a4d63f01 100644
--- a/compiler/debug/dwarf/debug_line_opcode_writer.h
+++ b/compiler/debug/dwarf/debug_line_opcode_writer.h
@@ -36,7 +36,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> {
public:
static constexpr int kOpcodeBase = 13;
- static constexpr bool kDefaultIsStmt = true;
+ static constexpr bool kDefaultIsStmt = false;
static constexpr int kLineBase = -5;
static constexpr int kLineRange = 14;
@@ -81,8 +81,11 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> {
this->PushUleb128(column);
}
- void NegateStmt() {
- this->PushUint8(DW_LNS_negate_stmt);
+ void SetIsStmt(bool is_stmt) {
+ if (is_stmt_ != is_stmt) {
+ this->PushUint8(DW_LNS_negate_stmt);
+ is_stmt_ = is_stmt;
+ }
}
void SetBasicBlock() {
@@ -112,6 +115,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> {
current_address_ = 0;
current_file_ = 1;
current_line_ = 1;
+ is_stmt_ = kDefaultIsStmt;
}
// Uncoditionally set address using the long encoding.
@@ -227,7 +231,8 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> {
code_factor_bits_(codeFactorBits),
current_address_(0),
current_file_(1),
- current_line_(1) {
+ current_line_(1),
+ is_stmt_(kDefaultIsStmt) {
}
private:
@@ -244,6 +249,7 @@ class DebugLineOpCodeWriter FINAL : private Writer<Vector> {
uint64_t current_address_;
int current_file_;
int current_line_;
+ bool is_stmt_;
std::vector<uintptr_t> patch_locations_;
DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter);
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index e455d0d617..2ba3af5e10 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -217,7 +217,9 @@ TEST_F(DwarfTest, DebugLine) {
DW_CHECK_NEXT("Advance Line by 2 to 3");
opcodes.SetColumn(4);
DW_CHECK_NEXT("Set column to 4");
- opcodes.NegateStmt();
+ opcodes.SetIsStmt(true);
+ DW_CHECK_NEXT("Set is_stmt to 1");
+ opcodes.SetIsStmt(false);
DW_CHECK_NEXT("Set is_stmt to 0");
opcodes.SetBasicBlock();
DW_CHECK_NEXT("Set basic block");
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index d3859ca752..11be4e9844 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -184,6 +184,10 @@ class ElfDebugLineWriter {
// Generate mapping opcodes from PC to Java lines.
if (file_index != 0) {
+ // If the method was not compiled as native-debuggable, we still generate all available
+ // lines, but we try to prevent the debugger from stepping and setting breakpoints since
+ // the information is too inaccurate for that (breakpoints would be set after the calls).
+ const bool default_is_stmt = mi->is_native_debuggable;
bool first = true;
for (SrcMapElem pc2dex : pc2dex_map) {
uint32_t pc = pc2dex.from_;
@@ -205,13 +209,14 @@ class ElfDebugLineWriter {
// Assume that any preceding code is prologue.
int first_line = dex2line_map.front().line_;
// Prologue is not a sensible place for a breakpoint.
- opcodes.NegateStmt();
+ opcodes.SetIsStmt(false);
opcodes.AddRow(method_address, first_line);
- opcodes.NegateStmt();
opcodes.SetPrologueEnd();
}
+ opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
} else if (line != opcodes.CurrentLine()) {
+ opcodes.SetIsStmt(default_is_stmt);
opcodes.AddRow(method_address + pc, line);
}
}
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 6b3dd8c528..bb09f7e814 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -30,6 +30,7 @@ struct MethodDebugInfo {
uint32_t access_flags;
const DexFile::CodeItem* code_item;
bool deduped;
+ bool is_native_debuggable;
uintptr_t low_pc;
uintptr_t high_pc;
CompiledMethod* compiled_method;
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index a220959288..4db82a638d 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -159,10 +159,16 @@ class CompilerOptions FINAL {
size_t GetInlineDepthLimit() const {
return inline_depth_limit_;
}
+ void SetInlineDepthLimit(size_t limit) {
+ inline_depth_limit_ = limit;
+ }
size_t GetInlineMaxCodeUnits() const {
return inline_max_code_units_;
}
+ void SetInlineMaxCodeUnits(size_t units) {
+ inline_max_code_units_ = units;
+ }
double GetTopKProfileThreshold() const {
return top_k_profile_threshold_;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3b622b5451..91579e9daf 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -24,6 +24,7 @@
#include "class_linker-inl.h"
#include "common_compiler_test.h"
#include "debug/method_debug_info.h"
+#include "driver/compiler_options.h"
#include "elf_writer.h"
#include "elf_writer_quick.h"
#include "gc/space/image_space.h"
@@ -48,8 +49,12 @@ class ImageTest : public CommonCompilerTest {
};
void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) {
- // TODO: Test does not currently work with optimizing.
- CreateCompilerDriver(Compiler::kQuick, kRuntimeISA);
+ CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U);
+
+ // Set inline filter values.
+ compiler_options_->SetInlineDepthLimit(CompilerOptions::kDefaultInlineDepthLimit);
+ compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits);
+
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
// Enable write for dex2dex.
for (const DexFile* dex_file : class_linker->GetBootClassPath()) {
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 871435b85f..b1b971f6ba 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -266,17 +266,9 @@ bool ImageWriter::Write(int image_fd,
<< PrettyDuration(NanoTime() - compress_start_time);
}
- // Write header first, as uncompressed.
- image_header->data_size_ = data_size;
- if (!image_file->WriteFully(image_info.image_->Begin(), sizeof(ImageHeader))) {
- PLOG(ERROR) << "Failed to write image file header " << image_filename;
- image_file->Erase();
- return false;
- }
-
// Write out the image + fields + methods.
const bool is_compressed = compressed_data != nullptr;
- if (!image_file->WriteFully(image_data_to_write, data_size)) {
+ if (!image_file->PwriteFully(image_data_to_write, data_size, sizeof(ImageHeader))) {
PLOG(ERROR) << "Failed to write image file data " << image_filename;
image_file->Erase();
return false;
@@ -291,13 +283,33 @@ bool ImageWriter::Write(int image_fd,
if (!is_compressed) {
CHECK_EQ(bitmap_position_in_file, bitmap_section.Offset());
}
- if (!image_file->Write(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
- bitmap_section.Size(),
- bitmap_position_in_file)) {
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_bitmap_->Begin()),
+ bitmap_section.Size(),
+ bitmap_position_in_file)) {
PLOG(ERROR) << "Failed to write image file " << image_filename;
image_file->Erase();
return false;
}
+
+ int err = image_file->Flush();
+ if (err < 0) {
+ PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err;
+ image_file->Erase();
+ return false;
+ }
+
+ // Write header last in case the compiler gets killed in the middle of image writing.
+ // We do not want to have a corrupted image with a valid header.
+ // The header is uncompressed since it contains whether the image is compressed or not.
+ image_header->data_size_ = data_size;
+ if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_->Begin()),
+ sizeof(ImageHeader),
+ 0)) {
+ PLOG(ERROR) << "Failed to write image file header " << image_filename;
+ image_file->Erase();
+ return false;
+ }
+
CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
static_cast<size_t>(image_file->GetLength()));
if (image_file->FlushCloseOrErase() != 0) {
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 9a779464f6..cda2e274ce 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -169,13 +169,14 @@ JitCompiler::JitCompiler() {
compiler_driver_->SetDedupeEnabled(false);
compiler_driver_->SetSupportBootImageFixup(false);
+ size_t thread_count = compiler_driver_->GetThreadCount();
if (compiler_options_->GetGenerateDebugInfo()) {
#ifdef __ANDROID__
const char* prefix = "/data/misc/trace";
#else
const char* prefix = "/tmp";
#endif
- DCHECK_EQ(compiler_driver_->GetThreadCount(), 1u)
+ DCHECK_EQ(thread_count, 1u)
<< "Generating debug info only works with one compiler thread";
std::string perf_filename = std::string(prefix) + "/perf-" + std::to_string(getpid()) + ".map";
perf_file_.reset(OS::CreateEmptyFileWriteOnly(perf_filename.c_str()));
@@ -184,6 +185,10 @@ JitCompiler::JitCompiler() {
" Are you on a user build? Perf only works on userdebug/eng builds";
}
}
+
+ size_t inline_depth_limit = compiler_driver_->GetCompilerOptions().GetInlineDepthLimit();
+ DCHECK_LT(thread_count * inline_depth_limit, std::numeric_limits<uint16_t>::max())
+ << "ProfilingInfo's inline counter can potentially overflow";
}
JitCompiler::~JitCompiler() {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index c60b02a227..50f5aba061 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -806,7 +806,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
}
}
- if (writer_->compiler_driver_->GetCompilerOptions().GenerateAnyDebugInfo()) {
+ const CompilerOptions& compiler_options = writer_->compiler_driver_->GetCompilerOptions();
+ if (compiler_options.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;
@@ -817,6 +818,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
it.GetMethodAccessFlags(),
it.GetMethodCodeItem(),
deduped,
+ compiler_options.GetNativeDebuggable(),
quick_code_start,
quick_code_start + code_size,
compiled_method});
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 3e3719e6ea..bbdac262c4 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -28,6 +28,8 @@
#include "driver/dex_compilation_unit.h"
#include "instruction_simplifier.h"
#include "intrinsics.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "nodes.h"
@@ -220,6 +222,20 @@ static uint32_t FindClassIndexIn(mirror::Class* cls,
return index;
}
+class ScopedProfilingInfoInlineUse {
+ public:
+ explicit ScopedProfilingInfoInlineUse(ArtMethod* method) : method_(method) {
+ Runtime::Current()->GetJit()->GetCodeCache()->NotifyInliningOf(method_, Thread::Current());
+ }
+
+ ~ScopedProfilingInfoInlineUse() {
+ Runtime::Current()->GetJit()->GetCodeCache()->DoneInlining(method_, Thread::Current());
+ }
+
+ private:
+ ArtMethod* const method_;
+};
+
bool HInliner::TryInline(HInvoke* invoke_instruction) {
if (invoke_instruction->IsInvokeUnresolved()) {
return false; // Don't bother to move further if we know the method is unresolved.
@@ -272,29 +288,32 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
// Check if we can use an inline cache.
ArtMethod* caller = graph_->GetArtMethod();
size_t pointer_size = class_linker->GetImagePointerSize();
- // Under JIT, we should always know the caller.
- DCHECK(!Runtime::Current()->UseJit() || (caller != nullptr));
- if (caller != nullptr && caller->GetProfilingInfo(pointer_size) != nullptr) {
+ if (Runtime::Current()->UseJit()) {
+ // Under JIT, we should always know the caller.
+ DCHECK(caller != nullptr);
+ ScopedProfilingInfoInlineUse spiis(caller);
ProfilingInfo* profiling_info = caller->GetProfilingInfo(pointer_size);
- const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc());
- if (ic.IsUnitialized()) {
- VLOG(compiler) << "Interface or virtual call to "
- << PrettyMethod(method_index, caller_dex_file)
- << " is not hit and not inlined";
- return false;
- } else if (ic.IsMonomorphic()) {
- MaybeRecordStat(kMonomorphicCall);
- return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
- } else if (ic.IsPolymorphic()) {
- MaybeRecordStat(kPolymorphicCall);
- return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
- } else {
- DCHECK(ic.IsMegamorphic());
- VLOG(compiler) << "Interface or virtual call to "
- << PrettyMethod(method_index, caller_dex_file)
- << " is megamorphic and not inlined";
- MaybeRecordStat(kMegamorphicCall);
- return false;
+ if (profiling_info != nullptr) {
+ const InlineCache& ic = *profiling_info->GetInlineCache(invoke_instruction->GetDexPc());
+ if (ic.IsUnitialized()) {
+ VLOG(compiler) << "Interface or virtual call to "
+ << PrettyMethod(method_index, caller_dex_file)
+ << " is not hit and not inlined";
+ return false;
+ } else if (ic.IsMonomorphic()) {
+ MaybeRecordStat(kMonomorphicCall);
+ return TryInlineMonomorphicCall(invoke_instruction, resolved_method, ic);
+ } else if (ic.IsPolymorphic()) {
+ MaybeRecordStat(kPolymorphicCall);
+ return TryInlinePolymorphicCall(invoke_instruction, resolved_method, ic);
+ } else {
+ DCHECK(ic.IsMegamorphic());
+ VLOG(compiler) << "Interface or virtual call to "
+ << PrettyMethod(method_index, caller_dex_file)
+ << " is megamorphic and not inlined";
+ MaybeRecordStat(kMegamorphicCall);
+ return false;
+ }
}
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index f8a9a94e62..b95ece5a31 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -94,6 +94,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
+ void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1594,6 +1595,12 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) {
invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
}
+void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) {
+ uint32_t dex_pc = invoke->GetDexPc();
+ HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc);
+ invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, mem_barrier);
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
switch (instruction->GetIntrinsic()) {
case Intrinsics::kStringEquals:
@@ -1626,6 +1633,15 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
case Intrinsics::kDoubleDoubleToLongBits:
SimplifyFP2Int(instruction);
break;
+ case Intrinsics::kUnsafeLoadFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny);
+ break;
+ case Intrinsics::kUnsafeStoreFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kAnyStore);
+ break;
+ case Intrinsics::kUnsafeFullFence:
+ SimplifyMemBarrier(instruction, MemBarrierKind::kAnyAny);
+ break;
default:
break;
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 0cec5ccfd3..3da82851a6 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -231,7 +231,10 @@ UNREACHABLE_INTRINSIC(Arch, LongRotateRight) \
UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \
UNREACHABLE_INTRINSIC(Arch, LongCompare) \
UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \
-UNREACHABLE_INTRINSIC(Arch, LongSignum)
+UNREACHABLE_INTRINSIC(Arch, LongSignum) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \
+UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence)
} // namespace art
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 34ed575bd0..4b94c94f39 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -2011,9 +2011,6 @@ UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(ARM, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(ARM, UnsafeFullFence)
UNREACHABLE_INTRINSICS(ARM)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index b65445766f..5de2306506 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1959,9 +1959,6 @@ UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(ARM64, UnsafeFullFence)
UNREACHABLE_INTRINSICS(ARM64)
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 697b8fe882..e159701a46 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1838,9 +1838,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeFullFence)
UNREACHABLE_INTRINSICS(MIPS)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 33096cac09..450398fbfc 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1736,9 +1736,6 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeFullFence)
UNREACHABLE_INTRINSICS(MIPS64)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 065866396b..95fdb9b3f6 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2646,9 +2646,6 @@ UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(X86, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(X86, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(X86, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(X86, UnsafeFullFence)
UNREACHABLE_INTRINSICS(X86)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 13a4bd08af..9e568f7b4f 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2724,9 +2724,6 @@ UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndAddLong)
UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetInt)
UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeLoadFence)
-UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeStoreFence)
-UNIMPLEMENTED_INTRINSIC(X86_64, UnsafeFullFence)
UNREACHABLE_INTRINSICS(X86_64)
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b684cc697f..ecb690ff62 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
+#include "dex_instruction-inl.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b49b91d984..42f22afd80 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -913,7 +913,8 @@ bool OptimizingCompiler::JitCompile(Thread* self,
return false;
}
- if (GetCompilerDriver()->GetCompilerOptions().GetGenerateDebugInfo()) {
+ const CompilerOptions& compiler_options = GetCompilerDriver()->GetCompilerOptions();
+ if (compiler_options.GetGenerateDebugInfo()) {
const auto* method_header = reinterpret_cast<const OatQuickMethodHeader*>(code);
const uintptr_t code_address = reinterpret_cast<uintptr_t>(method_header->GetCode());
CompiledMethod compiled_method(
@@ -936,6 +937,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
access_flags,
code_item,
false, // deduped.
+ compiler_options.GetNativeDebuggable(),
code_address,
code_address + code_allocator.GetSize(),
&compiled_method
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 374d0d37a1..44e7fc9017 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1034,9 +1034,11 @@ class Dex2Oat FINAL {
key_value_store_->Put(
OatHeader::kDebuggableKey,
compiler_options_->debuggable_ ? OatHeader::kTrueValue : OatHeader::kFalseValue);
- key_value_store_->Put(
- OatHeader::kExtractOnlyKey,
- compiler_options_->IsExtractOnly() ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ if (compiler_options_->IsExtractOnly()) {
+ key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kExtractOnlyValue);
+ } else if (UseProfileGuidedCompilation()) {
+ key_value_store_->Put(OatHeader::kCompilationType, OatHeader::kProfileGuideCompiledValue);
+ }
}
// Parse the arguments from the command line. In case of an unrecognized option or impossible
@@ -1891,13 +1893,6 @@ class Dex2Oat FINAL {
return success;
}
- bool ShouldCompileBasedOnProfiles() const {
- DCHECK(UseProfileGuidedCompilation());
- // If we are given a profile, compile only if we have some data in it.
- return (profile_compilation_info_ != nullptr) &&
- (profile_compilation_info_->GetNumberOfMethods() != 0);
- }
-
private:
template <typename T>
static std::vector<T*> MakeNonOwningPointerVector(const std::vector<std::unique_ptr<T>>& src) {
@@ -2595,16 +2590,11 @@ static int dex2oat(int argc, char** argv) {
// Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
dex2oat->ParseArgs(argc, argv);
- // Process profile information and assess if we need to do a profile guided compilation.
+ // If needed, process profile information for profile guided compilation.
// This operation involves I/O.
if (dex2oat->UseProfileGuidedCompilation()) {
- if (dex2oat->LoadProfile()) {
- if (!dex2oat->ShouldCompileBasedOnProfiles()) {
- LOG(INFO) << "Skipped compilation because of insignificant profile delta";
- return EXIT_SUCCESS;
- }
- } else {
- LOG(WARNING) << "Failed to process profile files";
+ if (!dex2oat->LoadProfile()) {
+ LOG(ERROR) << "Failed to process profile file";
return EXIT_FAILURE;
}
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index b52f30ec33..6c6c807588 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -98,6 +98,7 @@ const DexFile* OpenDexFile(const OatFile::OatDexFile* oat_dex_file, std::string*
return ret;
}
+template <typename ElfTypes>
class OatSymbolizer FINAL {
public:
OatSymbolizer(const OatFile* oat_file, const std::string& output_name) :
@@ -121,7 +122,7 @@ class OatSymbolizer FINAL {
File* elf_file = OS::CreateEmptyFile(output_name_.c_str());
std::unique_ptr<BufferedOutputStream> output_stream(
MakeUnique<BufferedOutputStream>(MakeUnique<FileOutputStream>(elf_file)));
- builder_.reset(new ElfBuilder<ElfTypes32>(isa, features, output_stream.get()));
+ builder_.reset(new ElfBuilder<ElfTypes>(isa, features, output_stream.get()));
builder_->Start();
@@ -151,14 +152,14 @@ class OatSymbolizer FINAL {
elf_file->GetPath(), rodata_size, text_size, oat_file_->BssSize());
builder_->WriteDynamicSection();
- Walk(&art::OatSymbolizer::RegisterForDedup);
+ Walk(&art::OatSymbolizer<ElfTypes>::RegisterForDedup);
NormalizeState();
strtab->Start();
strtab->Write(""); // strtab should start with empty string.
AddTrampolineSymbols();
- Walk(&art::OatSymbolizer::AddSymbol);
+ Walk(&art::OatSymbolizer<ElfTypes>::AddSymbol);
strtab->End();
symtab->Start();
@@ -346,7 +347,7 @@ class OatSymbolizer FINAL {
}
const OatFile* oat_file_;
- std::unique_ptr<ElfBuilder<ElfTypes32> > builder_;
+ std::unique_ptr<ElfBuilder<ElfTypes> > builder_;
std::unordered_map<uint32_t, uint32_t> state_;
const std::string output_name_;
};
@@ -2544,8 +2545,17 @@ static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
return EXIT_FAILURE;
}
- OatSymbolizer oat_symbolizer(oat_file, output_name);
- if (!oat_symbolizer.Symbolize()) {
+ bool result;
+ // Try to produce an ELF file of the same type. This is finicky, as we have used 32-bit ELF
+ // files for 64-bit code in the past.
+ if (Is64BitInstructionSet(oat_file->GetOatHeader().GetInstructionSet())) {
+ OatSymbolizer<ElfTypes64> oat_symbolizer(oat_file, output_name);
+ result = oat_symbolizer.Symbolize();
+ } else {
+ OatSymbolizer<ElfTypes32> oat_symbolizer(oat_file, output_name);
+ result = oat_symbolizer.Symbolize();
+ }
+ if (!result) {
fprintf(stderr, "Failed to symbolize\n");
return EXIT_FAILURE;
}
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 4672948f31..e4097dd3de 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -234,21 +234,34 @@ bool FdFile::PreadFully(void* buffer, size_t byte_count, size_t offset) {
return ReadFullyGeneric<pread>(fd_, buffer, byte_count, offset);
}
-bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+template <bool kUseOffset>
+bool FdFile::WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset) {
DCHECK(!read_only_mode_);
- const char* ptr = static_cast<const char*>(buffer);
moveTo(GuardState::kBase, GuardState::kClosed, "Writing into closed file.");
+ DCHECK(kUseOffset || offset == 0u);
+ const char* ptr = static_cast<const char*>(buffer);
while (byte_count > 0) {
- ssize_t bytes_written = TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
+ ssize_t bytes_written = kUseOffset
+ ? TEMP_FAILURE_RETRY(pwrite(fd_, ptr, byte_count, offset))
+ : TEMP_FAILURE_RETRY(write(fd_, ptr, byte_count));
if (bytes_written == -1) {
return false;
}
byte_count -= bytes_written; // Reduce the number of remaining bytes.
ptr += bytes_written; // Move the buffer forward.
+ offset += static_cast<size_t>(bytes_written);
}
return true;
}
+bool FdFile::PwriteFully(const void* buffer, size_t byte_count, size_t offset) {
+ return WriteFullyGeneric<true>(buffer, byte_count, offset);
+}
+
+bool FdFile::WriteFully(const void* buffer, size_t byte_count) {
+ return WriteFullyGeneric<false>(buffer, byte_count, 0u);
+}
+
bool FdFile::Copy(FdFile* input_file, int64_t offset, int64_t size) {
DCHECK(!read_only_mode_);
off_t off = static_cast<off_t>(offset);
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index 8040afe9b7..16cd44f4ef 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -79,6 +79,7 @@ class FdFile : public RandomAccessFile {
bool ReadFully(void* buffer, size_t byte_count) WARN_UNUSED;
bool PreadFully(void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
bool WriteFully(const void* buffer, size_t byte_count) WARN_UNUSED;
+ bool PwriteFully(const void* buffer, size_t byte_count, size_t offset) WARN_UNUSED;
// Copy data from another file.
bool Copy(FdFile* input_file, int64_t offset, int64_t size);
@@ -119,6 +120,9 @@ class FdFile : public RandomAccessFile {
GuardState guard_state_;
private:
+ template <bool kUseOffset>
+ bool WriteFullyGeneric(const void* buffer, size_t byte_count, size_t offset);
+
int fd_;
std::string file_path_;
bool auto_close_;
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index ecf607c892..9bc87e5bb9 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -110,6 +110,34 @@ TEST_F(FdFileTest, ReadFullyWithOffset) {
ASSERT_EQ(file.Close(), 0);
}
+TEST_F(FdFileTest, ReadWriteFullyWithOffset) {
+ // New scratch file, zero-length.
+ art::ScratchFile tmp;
+ FdFile file;
+ ASSERT_TRUE(file.Open(tmp.GetFilename(), O_RDWR));
+ EXPECT_GE(file.Fd(), 0);
+ EXPECT_TRUE(file.IsOpened());
+
+ const char* test_string = "This is a test string";
+ size_t length = strlen(test_string) + 1;
+ const size_t offset = 12;
+ std::unique_ptr<char[]> offset_read_string(new char[length]);
+ std::unique_ptr<char[]> read_string(new char[length]);
+
+ // Write scratch data to file that we can read back into.
+ EXPECT_TRUE(file.PwriteFully(test_string, length, offset));
+ ASSERT_EQ(file.Flush(), 0);
+
+ // Test reading both the offsets.
+ EXPECT_TRUE(file.PreadFully(&offset_read_string[0], length, offset));
+ EXPECT_STREQ(test_string, &offset_read_string[0]);
+
+ EXPECT_TRUE(file.PreadFully(&read_string[0], length, 0u));
+ EXPECT_NE(memcmp(&read_string[0], test_string, length), 0);
+
+ ASSERT_EQ(file.Close(), 0);
+}
+
TEST_F(FdFileTest, Copy) {
art::ScratchFile src_tmp;
FdFile src;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 01d140a0ba..d51a1f7ecc 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -7714,7 +7714,10 @@ std::set<DexCacheResolvedClasses> ClassLinker::GetResolvedClasses(bool ignore_bo
}
++num_resolved;
DCHECK(!klass->IsProxyClass());
- DCHECK(klass->IsResolved());
+ if (!klass->IsResolved()) {
+ DCHECK(klass->IsErroneous());
+ continue;
+ }
mirror::DexCache* klass_dex_cache = klass->GetDexCache();
if (klass_dex_cache == dex_cache) {
const size_t class_def_idx = klass->GetDexClassDefIndex();
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 3df9101613..729957f318 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -406,6 +406,7 @@ void CommonRuntimeTestImpl::TearDown() {
int rmdir_cache_result = rmdir(dalvik_cache_.c_str());
ASSERT_EQ(0, rmdir_cache_result);
TearDownAndroidData(android_data_, true);
+ dalvik_cache_.clear();
// icu4c has a fixed 10-element array "gCommonICUDataArray".
// If we run > 10 tests, we fill that array and u_setCommonData fails.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index faa3d3bc14..2e5b599940 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -59,6 +59,8 @@
#include "heap-inl.h"
#include "image.h"
#include "intern_table.h"
+#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
@@ -2668,6 +2670,12 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
// permanantly disabled. b/17942071
concurrent_start_bytes_ = std::numeric_limits<size_t>::max();
}
+
+ if ((gc_type == collector::kGcTypeFull) && runtime->UseJit()) {
+ // It's time to clear all inline caches, in case some classes can be unloaded.
+ runtime->GetJit()->GetCodeCache()->ClearGcRootsInInlineCaches(self);
+ }
+
CHECK(collector != nullptr)
<< "Could not find garbage collector with collector_type="
<< static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index a4e558728e..9ecd391e4d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1535,50 +1535,31 @@ void ImageSpace::CreateMultiImageLocations(const std::string& input_image_file_n
// images[0] is f/c/d/e.art
// ----------------------------------------------
// images[1] is g/h/i/j.art -> /a/b/h/i/j.art
-
- // Derive pattern.
- std::vector<std::string> left;
- Split(input_image_file_name, '/', &left);
- std::vector<std::string> right;
- Split(images[0], '/', &right);
-
- size_t common = 1;
- while (common < left.size() && common < right.size()) {
- if (left[left.size() - common - 1] != right[right.size() - common - 1]) {
- break;
- }
- common++;
- }
-
- std::vector<std::string> prefix_vector(left.begin(), left.end() - common);
- std::string common_prefix = Join(prefix_vector, '/');
- if (!common_prefix.empty() && common_prefix[0] != '/' && input_image_file_name[0] == '/') {
- common_prefix = "/" + common_prefix;
- }
+ const std::string& first_image = images[0];
+ // Length of common suffix.
+ size_t common = 0;
+ while (common < input_image_file_name.size() &&
+ common < first_image.size() &&
+ *(input_image_file_name.end() - common - 1) == *(first_image.end() - common - 1)) {
+ ++common;
+ }
+ // We want to replace the prefix of the input image with the prefix of the boot class path.
+ // This handles the case where the image file contains @ separators.
+ // Example image_file_name is oats/system@framework@boot.art
+ // images[0] is .../arm/boot.art
+ // means that the image name prefix will be oats/system@framework@
+ // so that the other images are openable.
+ const size_t old_prefix_length = first_image.size() - common;
+ const std::string new_prefix = input_image_file_name.substr(
+ 0,
+ input_image_file_name.size() - common);
// Apply pattern to images[1] .. images[n].
for (size_t i = 1; i < images.size(); ++i) {
- std::string image = images[i];
-
- size_t rslash = std::string::npos;
- for (size_t j = 0; j < common; ++j) {
- if (rslash != std::string::npos) {
- rslash--;
- }
-
- rslash = image.rfind('/', rslash);
- if (rslash == std::string::npos) {
- rslash = 0;
- }
- if (rslash == 0) {
- break;
- }
- }
- std::string image_part = image.substr(rslash);
-
- std::string new_image = common_prefix + (StartsWith(image_part, "/") ? "" : "/") +
- image_part;
- image_file_names->push_back(new_image);
+ const std::string& image = images[i];
+ CHECK_GT(image.length(), old_prefix_length);
+ std::string suffix = image.substr(old_prefix_length);
+ image_file_names->push_back(new_prefix + suffix);
}
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index a3b99e3dda..7dbd89cedb 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -188,9 +188,11 @@ void Jit::DeleteThreadPool() {
}
void Jit::StartProfileSaver(const std::string& filename,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (save_profiling_info_) {
- ProfileSaver::Start(filename, code_cache_.get(), code_paths);
+ ProfileSaver::Start(filename, code_cache_.get(), code_paths, foreign_dex_profile_path, app_dir);
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 3f54192d9f..ee416d8772 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -70,7 +70,17 @@ class Jit {
return instrumentation_cache_.get();
}
- void StartProfileSaver(const std::string& filename, const std::vector<std::string>& code_paths);
+ // Starts the profile saver if the config options allow profile recording.
+ // The profile will be stored in the specified `filename` and will contain
+ // information collected from the given `code_paths` (a set of dex locations).
+ // The `foreign_dex_profile_path` is the path where the saver will put the
+ // profile markers for loaded dex files which are not owned by the application.
+ // The `app_dir` is the application directory and is used to decide which
+ // dex files belong to the application.
+ void StartProfileSaver(const std::string& filename,
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
void StopProfileSaver();
void DumpForSigQuit(std::ostream& os) {
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 0b0f926ed1..050bb68336 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -297,6 +297,15 @@ void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
}
}
+void JitCodeCache::ClearGcRootsInInlineCaches(Thread* self) {
+ MutexLock mu(self, lock_);
+ for (ProfilingInfo* info : profiling_infos_) {
+ if (!info->IsInUseByCompiler()) {
+ info->ClearGcRootsInInlineCaches();
+ }
+ }
+}
+
uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
ArtMethod* method,
const uint8_t* mapping_table,
@@ -679,7 +688,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
// Also remove the saved entry point from the ProfilingInfo objects.
for (ProfilingInfo* info : profiling_infos_) {
const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
- if (!ContainsPc(ptr) && !info->IsMethodBeingCompiled()) {
+ if (!ContainsPc(ptr) && !info->IsInUseByCompiler()) {
info->GetMethod()->SetProfilingInfo(nullptr);
}
info->SetSavedEntryPoint(nullptr);
@@ -731,7 +740,7 @@ void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
// code cache collection.
if (ContainsPc(ptr) && info->GetMethod()->GetProfilingInfo(sizeof(void*)) == nullptr) {
// We clear the inline caches as classes in it might be stalled.
- info->ClearInlineCaches();
+ info->ClearGcRootsInInlineCaches();
// Do a fence to make sure the clearing is seen before attaching to the method.
QuasiAtomic::ThreadFenceRelease();
info->GetMethod()->SetProfilingInfo(info);
@@ -919,6 +928,22 @@ bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr
return true;
}
+void JitCodeCache::NotifyInliningOf(ArtMethod* method, Thread* self) {
+ MutexLock mu(self, lock_);
+ ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
+ if (info != nullptr) {
+ info->IncrementInlineUse();
+ }
+}
+
+void JitCodeCache::DoneInlining(ArtMethod* method, Thread* self) {
+ MutexLock mu(self, lock_);
+ ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
+ if (info != nullptr) {
+ info->DecrementInlineUse();
+ }
+}
+
void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self ATTRIBUTE_UNUSED) {
ProfilingInfo* info = method->GetProfilingInfo(sizeof(void*));
DCHECK(info->IsMethodBeingCompiled());
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 0bd4f7dd1b..113bebfa65 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -71,10 +71,18 @@ class JitCodeCache {
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
+ void NotifyInliningOf(ArtMethod* method, Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!lock_);
+
void DoneCompiling(ArtMethod* method, Thread* self)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
+ void DoneInlining(ArtMethod* method, Thread* self)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!lock_);
+
// Allocate and write code and its metadata to the code cache.
uint8_t* CommitCode(Thread* self,
ArtMethod* method,
@@ -143,6 +151,8 @@ class JitCodeCache {
REQUIRES(Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ void ClearGcRootsInInlineCaches(Thread* self) REQUIRES(!lock_);
+
// Create a 'ProfileInfo' for 'method'. If 'retry_allocation' is true,
// will collect and retry if the first allocation is unsuccessful.
ProfilingInfo* AddProfilingInfo(Thread* self,
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index bd581570ed..6fe17dbe15 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -16,6 +16,10 @@
#include "profile_saver.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
#include "art_method-inl.h"
#include "base/systrace.h"
#include "scoped_thread_state_change.h"
@@ -43,14 +47,31 @@ pthread_t ProfileSaver::profiler_pthread_ = 0U;
ProfileSaver::ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
: jit_code_cache_(jit_code_cache),
+ foreign_dex_profile_path_(foreign_dex_profile_path),
code_cache_last_update_time_ns_(0),
shutting_down_(false),
first_profile_(true),
wait_lock_("ProfileSaver wait lock"),
period_condition_("ProfileSaver period condition", wait_lock_) {
AddTrackedLocations(output_filename, code_paths);
+ app_data_dir_ = "";
+ if (!app_data_dir.empty()) {
+ // The application directory is used to determine which dex files are owned by app.
+ // Since it could be a symlink (e.g. /data/data instead of /data/user/0), and we
+ // don't have control over how the dex files are actually loaded (symlink or canonical path),
+ // store it's canonical form to be sure we use the same base when comparing.
+ UniqueCPtr<const char[]> app_data_dir_real_path(realpath(app_data_dir.c_str(), nullptr));
+ if (app_data_dir_real_path != nullptr) {
+ app_data_dir_.assign(app_data_dir_real_path.get());
+ } else {
+ LOG(WARNING) << "Failed to get the real path for app dir: " << app_data_dir_
+ << ". The app dir will not be used to determine which dex files belong to the app";
+ }
+ }
}
void ProfileSaver::Run() {
@@ -164,7 +185,9 @@ void* ProfileSaver::RunProfileSaverThread(void* arg) {
void ProfileSaver::Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths) {
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
DCHECK(Runtime::Current()->UseJit());
DCHECK(!output_filename.empty());
DCHECK(jit_code_cache != nullptr);
@@ -183,7 +206,11 @@ void ProfileSaver::Start(const std::string& output_filename,
VLOG(profiler) << "Starting profile saver using output file: " << output_filename
<< ". Tracking: " << Join(code_paths, ':');
- instance_ = new ProfileSaver(output_filename, jit_code_cache, code_paths);
+ instance_ = new ProfileSaver(output_filename,
+ jit_code_cache,
+ code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
// Create a new thread which does the saving.
CHECK_PTHREAD_CALL(
@@ -250,4 +277,97 @@ void ProfileSaver::AddTrackedLocations(const std::string& output_filename,
}
}
+void ProfileSaver::NotifyDexUse(const std::string& dex_location) {
+ std::set<std::string> app_code_paths;
+ std::string foreign_dex_profile_path;
+ std::string app_data_dir;
+ {
+ MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+ DCHECK(instance_ != nullptr);
+ // Make a copy so that we don't hold the lock while doing I/O.
+ for (const auto& it : instance_->tracked_dex_base_locations_) {
+ app_code_paths.insert(it.second.begin(), it.second.end());
+ }
+ foreign_dex_profile_path = instance_->foreign_dex_profile_path_;
+ app_data_dir = instance_->app_data_dir_;
+ }
+
+ MaybeRecordDexUseInternal(dex_location,
+ app_code_paths,
+ foreign_dex_profile_path,
+ app_data_dir);
+}
+
+void ProfileSaver::MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& app_code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir) {
+ if (dex_location.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use with an empty dex location.";
+ return;
+ }
+ if (foreign_dex_profile_path.empty()) {
+ LOG(WARNING) << "Asked to record foreign dex use without a valid profile path ";
+ return;
+ }
+
+ UniqueCPtr<const char[]> dex_location_real_path(realpath(dex_location.c_str(), nullptr));
+ if (dex_location_real_path == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << dex_location;
+ }
+ std::string dex_location_real_path_str((dex_location_real_path == nullptr)
+ ? dex_location.c_str()
+ : dex_location_real_path.get());
+
+ if (dex_location_real_path_str.compare(0, app_data_dir.length(), app_data_dir) == 0) {
+ // The dex location is under the application folder. Nothing to record.
+ return;
+ }
+
+ if (app_code_paths.find(dex_location) != app_code_paths.end()) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ // Do another round of checks with the real paths.
+ // Note that we could cache all the real locations in the saver (since it's an expensive
+ // operation). However we expect that app_code_paths is small (usually 1 element), and
+ // NotifyDexUse is called just a few times in the app lifetime. So we make the compromise
+ // to save some bytes of memory usage.
+ for (const auto& app_code_location : app_code_paths) {
+ UniqueCPtr<const char[]> real_app_code_location(realpath(app_code_location.c_str(), nullptr));
+ if (real_app_code_location == nullptr) {
+ PLOG(WARNING) << "Could not get realpath for " << app_code_location;
+ }
+ std::string real_app_code_location_str((real_app_code_location == nullptr)
+ ? app_code_location.c_str()
+ : real_app_code_location.get());
+ if (real_app_code_location_str == dex_location_real_path_str) {
+ // The dex location belongs to the application code paths. Nothing to record.
+ return;
+ }
+ }
+
+ // For foreign dex files we record a flag on disk. PackageManager will (potentially) take this
+ // into account when deciding how to optimize the loaded dex file.
+ // The expected flag name is the canonical path of the apk where '/' is substituted to '@'.
+ // (it needs to be kept in sync with
+ // frameworks/base/services/core/java/com/android/server/pm/PackageDexOptimizer.java)
+ std::replace(dex_location_real_path_str.begin(), dex_location_real_path_str.end(), '/', '@');
+ std::string flag_path = foreign_dex_profile_path + "/" + dex_location_real_path_str;
+ // No need to give any sort of access to flag_path. The system has enough permissions
+ // to test for its existence.
+ int fd = TEMP_FAILURE_RETRY(open(flag_path.c_str(), O_CREAT | O_EXCL, 0));
+ if (fd != -1) {
+ if (close(fd) != 0) {
+ PLOG(WARNING) << "Could not close file after flagging foreign dex use " << flag_path;
+ }
+ } else {
+ if (errno != EEXIST) {
+ // Another app could have already created the file.
+ PLOG(WARNING) << "Could not create foreign dex use mark " << flag_path;
+ }
+ }
+}
+
} // namespace art
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 21017c1acd..e7eab95f3d 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -30,7 +30,9 @@ class ProfileSaver {
// If the saver is already running it adds (output_filename, code_paths) to its tracked locations.
static void Start(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths)
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir)
REQUIRES(!Locks::profiler_lock_, !wait_lock_);
// Stops the profile saver thread.
@@ -42,10 +44,14 @@ class ProfileSaver {
// Returns true if the profile saver is started.
static bool IsStarted() REQUIRES(!Locks::profiler_lock_);
+ static void NotifyDexUse(const std::string& dex_location);
+
private:
ProfileSaver(const std::string& output_filename,
jit::JitCodeCache* jit_code_cache,
- const std::vector<std::string>& code_paths);
+ const std::vector<std::string>& code_paths,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
// NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
static void* RunProfileSaverThread(void* arg)
@@ -64,6 +70,12 @@ class ProfileSaver {
const std::vector<std::string>& code_paths)
REQUIRES(Locks::profiler_lock_);
+ static void MaybeRecordDexUseInternal(
+ const std::string& dex_location,
+ const std::set<std::string>& tracked_locations,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_data_dir);
+
// The only instance of the saver.
static ProfileSaver* instance_ GUARDED_BY(Locks::profiler_lock_);
// Profile saver thread.
@@ -72,6 +84,8 @@ class ProfileSaver {
jit::JitCodeCache* jit_code_cache_;
SafeMap<std::string, std::set<std::string>> tracked_dex_base_locations_
GUARDED_BY(Locks::profiler_lock_);
+ std::string foreign_dex_profile_path_;
+ std::string app_data_dir_;
uint64_t code_cache_last_update_time_ns_;
bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
bool first_profile_ = true;
diff --git a/runtime/jit/profiling_info.cc b/runtime/jit/profiling_info.cc
index 3820592c4c..07c8051214 100644
--- a/runtime/jit/profiling_info.cc
+++ b/runtime/jit/profiling_info.cc
@@ -97,8 +97,8 @@ void ProfilingInfo::AddInvokeInfo(uint32_t dex_pc, mirror::Class* cls) {
}
}
}
- // Unsuccessfull - cache is full, making it megamorphic.
- DCHECK(cache->IsMegamorphic());
+ // Unsuccessfull - cache is full, making it megamorphic. We do not DCHECK it though,
+ // as the garbage collector might clear the entries concurrently.
}
} // namespace art
diff --git a/runtime/jit/profiling_info.h b/runtime/jit/profiling_info.h
index a8c056c7c9..73c1a1edb0 100644
--- a/runtime/jit/profiling_info.h
+++ b/runtime/jit/profiling_info.h
@@ -134,8 +134,27 @@ class ProfilingInfo {
return saved_entry_point_;
}
- void ClearInlineCaches() {
- memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
+ void ClearGcRootsInInlineCaches() {
+ for (size_t i = 0; i < number_of_inline_caches_; ++i) {
+ InlineCache* cache = &cache_[i];
+ memset(&cache->classes_[0],
+ 0,
+ InlineCache::kIndividualCacheSize * sizeof(GcRoot<mirror::Class>));
+ }
+ }
+
+ void IncrementInlineUse() {
+ DCHECK_NE(current_inline_uses_, std::numeric_limits<uint16_t>::max());
+ current_inline_uses_++;
+ }
+
+ void DecrementInlineUse() {
+ DCHECK_GT(current_inline_uses_, 0);
+ current_inline_uses_--;
+ }
+
+ bool IsInUseByCompiler() const {
+ return IsMethodBeingCompiled() || (current_inline_uses_ > 0);
}
private:
@@ -143,8 +162,9 @@ class ProfilingInfo {
: number_of_inline_caches_(entries.size()),
method_(method),
is_method_being_compiled_(false),
+ current_inline_uses_(0),
saved_entry_point_(nullptr) {
- ClearInlineCaches();
+ memset(&cache_, 0, number_of_inline_caches_ * sizeof(InlineCache));
for (size_t i = 0; i < number_of_inline_caches_; ++i) {
cache_[i].dex_pc_ = entries[i];
}
@@ -161,6 +181,10 @@ class ProfilingInfo {
// TODO: Make the JIT code cache lock global.
bool is_method_being_compiled_;
+ // When the compiler inlines the method associated to this ProfilingInfo,
+ // it updates this counter so that the GC does not try to clear the inline caches.
+ uint16_t current_inline_uses_;
+
// Entry point of the corresponding ArtMethod, while the JIT code cache
// is poking for the liveness of compiled code.
const void* saved_entry_point_;
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 11156c6229..421641ce39 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -590,7 +590,19 @@ void MemMap::MadviseDontNeedAndZero() {
}
bool MemMap::Sync() {
- return msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+ bool result;
+ if (redzone_size_ != 0) {
+ // To avoid valgrind errors, temporarily lift the lower-end noaccess protection before passing
+ // it to msync() as it only accepts page-aligned base address, and exclude the higher-end
+ // noaccess protection from the msync range. b/27552451.
+ uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
+ MEMORY_TOOL_MAKE_DEFINED(base_begin, begin_ - base_begin);
+ result = msync(BaseBegin(), End() - base_begin, MS_SYNC) == 0;
+ MEMORY_TOOL_MAKE_NOACCESS(base_begin, begin_ - base_begin);
+ } else {
+ result = msync(BaseBegin(), BaseSize(), MS_SYNC) == 0;
+ }
+ return result;
}
bool MemMap::Protect(int prot) {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 6643ac2231..f1e0fa7b57 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -347,15 +347,14 @@ static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jobject cookie
static jint GetDexOptNeeded(JNIEnv* env,
const char* filename,
- const char* pkgname,
const char* instruction_set,
- const jboolean defer) {
+ const int target_compilation_type_mask) {
if ((filename == nullptr) || !OS::FileExists(filename)) {
LOG(ERROR) << "DexFile_getDexOptNeeded file '" << filename << "' does not exist";
ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
const char* message = (filename == nullptr) ? "<empty file name>" : filename;
env->ThrowNew(fnfe.get(), message);
- return OatFileAssistant::kNoDexOptNeeded;
+ return -1;
}
const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
@@ -363,73 +362,52 @@ static jint GetDexOptNeeded(JNIEnv* env,
ScopedLocalRef<jclass> iae(env, env->FindClass("java/lang/IllegalArgumentException"));
std::string message(StringPrintf("Instruction set %s is invalid.", instruction_set));
env->ThrowNew(iae.get(), message.c_str());
- return 0;
+ return -1;
}
// TODO: Verify the dex location is well formed, and throw an IOException if
// not?
-
- OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);
+ OatFileAssistant oat_file_assistant(filename, target_compilation_type_mask,
+ target_instruction_set, false);
// Always treat elements of the bootclasspath as up-to-date.
if (oat_file_assistant.IsInBootClassPath()) {
return OatFileAssistant::kNoDexOptNeeded;
}
- // TODO: Checking the profile should probably be done in the GetStatus()
- // function. We have it here because GetStatus() should not be copying
- // profile files. But who should be copying profile files?
- if (oat_file_assistant.OdexFileIsOutOfDate()) {
- // Needs recompile if profile has changed significantly.
- if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
- if (oat_file_assistant.IsProfileChangeSignificant()) {
- if (!defer) {
- oat_file_assistant.CopyProfileFile();
- }
- return OatFileAssistant::kDex2OatNeeded;
- } else if (oat_file_assistant.ProfileExists()
- && !oat_file_assistant.OldProfileExists()) {
- if (!defer) {
- oat_file_assistant.CopyProfileFile();
- }
- }
- }
- }
-
return oat_file_assistant.GetDexOptNeeded();
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
jclass,
jstring javaFilename,
- jstring javaPkgname,
jstring javaInstructionSet,
- jboolean defer) {
+ jint javaTargetCompilationTypeMask) {
ScopedUtfChars filename(env, javaFilename);
if (env->ExceptionCheck()) {
- return 0;
+ return -1;
}
- NullableScopedUtfChars pkgname(env, javaPkgname);
-
ScopedUtfChars instruction_set(env, javaInstructionSet);
if (env->ExceptionCheck()) {
- return 0;
+ return -1;
}
return GetDexOptNeeded(env,
filename.c_str(),
- pkgname.c_str(),
instruction_set.c_str(),
- defer);
+ javaTargetCompilationTypeMask);
}
-// public API, null pkgname
+// public API
static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
const char* instruction_set = GetInstructionSetString(kRuntimeISA);
ScopedUtfChars filename(env, javaFilename);
- jint status = GetDexOptNeeded(env, filename.c_str(), nullptr /* pkgname */,
- instruction_set, false /* defer */);
+ jint status = GetDexOptNeeded(
+ env,
+ filename.c_str(),
+ instruction_set,
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kProfileGuideCompilation);
return (status != OatFileAssistant::kNoDexOptNeeded) ? JNI_TRUE : JNI_FALSE;
}
@@ -445,7 +423,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)I"),
+ "(Ljava/lang/String;Ljava/lang/String;I)I"),
NATIVE_METHOD(DexFile, openDexFileNative,
"(Ljava/lang/String;"
"Ljava/lang/String;"
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index da4a891ff5..f6b2f21515 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -566,8 +566,9 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) {
static void VMRuntime_registerAppInfo(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED,
jstring profile_file,
- jstring app_dir ATTRIBUTE_UNUSED, // TODO: remove argument
- jobjectArray code_paths) {
+ jstring app_dir,
+ jobjectArray code_paths,
+ jstring foreign_dex_profile_path) {
std::vector<std::string> code_paths_vec;
int code_paths_length = env->GetArrayLength(code_paths);
for (int i = 0; i < code_paths_length; i++) {
@@ -581,7 +582,22 @@ static void VMRuntime_registerAppInfo(JNIEnv* env,
std::string profile_file_str(raw_profile_file);
env->ReleaseStringUTFChars(profile_file, raw_profile_file);
- Runtime::Current()->RegisterAppInfo(code_paths_vec, profile_file_str);
+ std::string foreign_dex_profile_path_str = "";
+ if (foreign_dex_profile_path != nullptr) {
+ const char* raw_foreign_dex_profile_path =
+ env->GetStringUTFChars(foreign_dex_profile_path, nullptr);
+ foreign_dex_profile_path_str.assign(raw_foreign_dex_profile_path);
+ env->ReleaseStringUTFChars(foreign_dex_profile_path, raw_foreign_dex_profile_path);
+ }
+
+ const char* raw_app_dir = env->GetStringUTFChars(app_dir, nullptr);
+ std::string app_dir_str(raw_app_dir);
+ env->ReleaseStringUTFChars(app_dir, raw_app_dir);
+
+ Runtime::Current()->RegisterAppInfo(code_paths_vec,
+ profile_file_str,
+ foreign_dex_profile_path_str,
+ app_dir_str);
}
static jboolean VMRuntime_isBootClassPathOnDisk(JNIEnv* env, jclass, jstring java_instruction_set) {
@@ -638,7 +654,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, isCheckJniEnabled, "!()Z"),
NATIVE_METHOD(VMRuntime, preloadDexCaches, "()V"),
NATIVE_METHOD(VMRuntime, registerAppInfo,
- "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V"),
+ "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(VMRuntime, isBootClassPathOnDisk, "(Ljava/lang/String;)Z"),
NATIVE_METHOD(VMRuntime, getCurrentInstructionSet, "()Ljava/lang/String;"),
};
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 4948558f84..2ac105291d 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,6 +29,8 @@ constexpr uint8_t OatHeader::kOatMagic[4];
constexpr uint8_t OatHeader::kOatVersion[4];
constexpr const char OatHeader::kTrueValue[];
constexpr const char OatHeader::kFalseValue[];
+constexpr const char OatHeader::kExtractOnlyValue[];
+constexpr const char OatHeader::kProfileGuideCompiledValue[];
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
@@ -467,12 +469,24 @@ bool OatHeader::IsDebuggable() const {
}
bool OatHeader::IsExtractOnly() const {
- return IsKeyEnabled(OatHeader::kExtractOnlyKey);
+ return KeyHasValue(kCompilationType,
+ kExtractOnlyValue,
+ sizeof(kExtractOnlyValue));
}
-bool OatHeader::IsKeyEnabled(const char* key) const {
+bool OatHeader::IsProfileGuideCompiled() const {
+ return KeyHasValue(kCompilationType,
+ kProfileGuideCompiledValue,
+ sizeof(kProfileGuideCompiledValue));
+}
+
+bool OatHeader::KeyHasValue(const char* key, const char* value, size_t value_size) const {
const char* key_value = GetStoreValueByKey(key);
- return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0);
+ return (key_value != nullptr && strncmp(key_value, value, value_size) == 0);
+}
+
+bool OatHeader::IsKeyEnabled(const char* key) const {
+ return KeyHasValue(key, kTrueValue, sizeof(kTrueValue));
}
void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
diff --git a/runtime/oat.h b/runtime/oat.h
index fde386f37e..0660e19ff4 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,12 +38,15 @@ class PACKED(4) OatHeader {
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
static constexpr const char* kDebuggableKey = "debuggable";
- static constexpr const char* kExtractOnlyKey = "extract-only";
+ static constexpr const char* kCompilationType = "compilation-type";
static constexpr const char* kClassPathKey = "classpath";
static constexpr const char* kBootClassPath = "bootclasspath";
static constexpr const char kTrueValue[] = "true";
static constexpr const char kFalseValue[] = "false";
+ static constexpr const char kExtractOnlyValue[] = "extract-only";
+ static constexpr const char kProfileGuideCompiledValue[] = "profile-guide";
+
static OatHeader* Create(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
@@ -108,8 +111,11 @@ class PACKED(4) OatHeader {
bool IsPic() const;
bool IsDebuggable() const;
bool IsExtractOnly() const;
+ bool IsProfileGuideCompiled() const;
private:
+ bool KeyHasValue(const char* key, const char* value, size_t value_size) const;
+
OatHeader(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
uint32_t dex_file_count,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c3895479b7..7155c79afb 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -169,7 +169,10 @@ bool OatFileBase::ComputeFields(uint8_t* requested_base,
return false;
}
if (requested_base != nullptr && begin_ != requested_base) {
- PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ // Host can fail this check. Do not dump there to avoid polluting the output.
+ if (kIsTargetBuild) {
+ PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
+ }
*error_msg = StringPrintf("Failed to find oatdata symbol at expected address: "
"oatdata=%p != expected=%p. See process maps in the log.",
begin_, requested_base);
@@ -1232,6 +1235,10 @@ bool OatFile::IsExtractOnly() const {
return GetOatHeader().IsExtractOnly();
}
+bool OatFile::IsProfileGuideCompiled() const {
+ return GetOatHeader().IsProfileGuideCompiled();
+}
+
static constexpr char kDexClassPathEncodingSeparator = '*';
std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index fb91a8cdff..1084253a88 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -93,6 +93,8 @@ class OatFile {
bool IsExtractOnly() const;
+ bool IsProfileGuideCompiled() const;
+
const std::string& GetLocation() const {
return location_;
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 262c932766..90712c625c 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,7 +36,6 @@
#include "image.h"
#include "oat.h"
#include "os.h"
-#include "profiler.h"
#include "runtime.h"
#include "scoped_thread_state_change.h"
#include "ScopedFd.h"
@@ -45,28 +44,19 @@
namespace art {
OatFileAssistant::OatFileAssistant(const char* dex_location,
+ const int target_compilation_type_mask,
const InstructionSet isa,
bool load_executable)
- : OatFileAssistant(dex_location, nullptr, isa, load_executable, nullptr) { }
+ : OatFileAssistant(dex_location, nullptr, target_compilation_type_mask, isa, load_executable)
+{ }
OatFileAssistant::OatFileAssistant(const char* dex_location,
const char* oat_location,
+ const int target_compilation_type_mask,
const InstructionSet isa,
bool load_executable)
- : OatFileAssistant(dex_location, oat_location, isa, load_executable, nullptr) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
- const InstructionSet isa,
- bool load_executable,
- const char* package_name)
- : OatFileAssistant(dex_location, nullptr, isa, load_executable, package_name) { }
-
-OatFileAssistant::OatFileAssistant(const char* dex_location,
- const char* oat_location,
- const InstructionSet isa,
- bool load_executable,
- const char* package_name)
- : isa_(isa), package_name_(package_name), load_executable_(load_executable) {
+ : target_compilation_type_mask_(target_compilation_type_mask), isa_(isa),
+ load_executable_(load_executable) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
dex_location_.assign(dex_location);
@@ -83,18 +73,6 @@ OatFileAssistant::OatFileAssistant(const char* dex_location,
cached_oat_file_name_attempted_ = true;
cached_oat_file_name_found_ = true;
}
-
- // If there is no package name given, we will not be able to find any
- // profiles associated with this dex location. Preemptively mark that to
- // be the case, rather than trying to find and load the profiles later.
- // Similarly, if profiling is disabled.
- if (package_name == nullptr
- || !Runtime::Current()->GetProfilerOptions().IsEnabled()) {
- profile_load_attempted_ = true;
- profile_load_succeeded_ = false;
- old_profile_load_attempted_ = true;
- old_profile_load_succeeded_ = false;
- }
}
OatFileAssistant::~OatFileAssistant() {
@@ -138,10 +116,23 @@ bool OatFileAssistant::Lock(std::string* error_msg) {
return true;
}
-OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
- // TODO: If the profiling code is ever restored, it's worth considering
- // whether we should check to see if the profile is out of date here.
+// Returns the compilation mode of the given oat file.
+static OatFileAssistant::CompilationType GetCompilationType(const OatFile& oat_file) {
+ if (oat_file.IsExtractOnly()) {
+ return OatFileAssistant::kExtractOnly;
+ }
+ if (oat_file.IsProfileGuideCompiled()) {
+ return OatFileAssistant::kProfileGuideCompilation;
+ }
+ // Assume that if the oat files is not extract-only or profile-guide compiled
+ // then it must be fully compiled.
+ // NB: this does not necessary mean that the oat file is actually fully compiled. It
+ // might have been compiled in a different way (e.g. interpret-only) which does
+ // not record a type in the header.
+ return OatFileAssistant::kFullCompilation;
+}
+OatFileAssistant::DexOptNeeded OatFileAssistant::GetDexOptNeeded() {
if (OatFileIsUpToDate() || OdexFileIsUpToDate()) {
return kNoDexOptNeeded;
}
@@ -419,6 +410,11 @@ OatFileAssistant::OatStatus OatFileAssistant::GivenOatFileStatus(const OatFile&
}
bool OatFileAssistant::GivenOatFileIsOutOfDate(const OatFile& file) {
+ // Verify the file satisfies the desired compilation type.
+ if ((target_compilation_type_mask_ & GetCompilationType(file)) == 0) {
+ return true;
+ }
+
// Verify the dex checksum.
// Note: GetOatDexFile will return null if the dex checksum doesn't match
// what we provide, which verifies the primary dex checksum for us.
@@ -541,104 +537,6 @@ bool OatFileAssistant::GivenOatFileIsUpToDate(const OatFile& file) {
return true;
}
-bool OatFileAssistant::ProfileExists() {
- return GetProfile() != nullptr;
-}
-
-bool OatFileAssistant::OldProfileExists() {
- return GetOldProfile() != nullptr;
-}
-
-// TODO: The IsProfileChangeSignificant implementation was copied from likely
-// bit-rotted code.
-bool OatFileAssistant::IsProfileChangeSignificant() {
- ProfileFile* profile = GetProfile();
- if (profile == nullptr) {
- return false;
- }
-
- ProfileFile* old_profile = GetOldProfile();
- if (old_profile == nullptr) {
- return false;
- }
-
- // TODO: The following code to compare two profile files should live with
- // the rest of the profiler code, not the oat file assistant code.
-
- // A change in profile is considered significant if X% (change_thr property)
- // of the top K% (compile_thr property) samples has changed.
- const ProfilerOptions& options = Runtime::Current()->GetProfilerOptions();
- const double top_k_threshold = options.GetTopKThreshold();
- const double change_threshold = options.GetTopKChangeThreshold();
- std::set<std::string> top_k, old_top_k;
- profile->GetTopKSamples(top_k, top_k_threshold);
- old_profile->GetTopKSamples(old_top_k, top_k_threshold);
- std::set<std::string> diff;
- std::set_difference(top_k.begin(), top_k.end(), old_top_k.begin(),
- old_top_k.end(), std::inserter(diff, diff.end()));
-
- // TODO: consider using the usedPercentage instead of the plain diff count.
- double change_percent = 100.0 * static_cast<double>(diff.size())
- / static_cast<double>(top_k.size());
- std::set<std::string>::iterator end = diff.end();
- for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
- VLOG(oat) << "Profile new in topK: " << *it;
- }
-
- if (change_percent > change_threshold) {
- VLOG(oat) << "Oat File Assistant: Profile for " << dex_location_
- << "has changed significantly: (top "
- << top_k_threshold << "% samples changed in proportion of "
- << change_percent << "%)";
- return true;
- }
- return false;
-}
-
-// TODO: The CopyProfileFile implementation was copied from likely bit-rotted
-// code.
-void OatFileAssistant::CopyProfileFile() {
- if (!ProfileExists()) {
- return;
- }
-
- std::string profile_name = ProfileFileName();
- std::string old_profile_name = OldProfileFileName();
-
- ScopedFd src(open(old_profile_name.c_str(), O_RDONLY));
- if (src.get() == -1) {
- PLOG(WARNING) << "Failed to open profile file " << old_profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- struct stat stat_src;
- if (fstat(src.get(), &stat_src) == -1) {
- PLOG(WARNING) << "Failed to get stats for profile file " << old_profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
- // Create the copy with rw------- (only accessible by system)
- ScopedFd dst(open(profile_name.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0600));
- if (dst.get() == -1) {
- PLOG(WARNING) << "Failed to create/write prev profile file " << profile_name
- << ". My uid:gid is " << getuid() << ":" << getgid();
- return;
- }
-
-#ifdef __linux__
- if (sendfile(dst.get(), src.get(), nullptr, stat_src.st_size) == -1) {
-#else
- off_t len;
- if (sendfile(dst.get(), src.get(), 0, &len, nullptr, 0) == -1) {
-#endif
- PLOG(WARNING) << "Failed to copy profile file " << old_profile_name
- << " to " << profile_name << ". My uid:gid is " << getuid()
- << ":" << getgid();
- }
-}
-
bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
std::string* error_msg) {
CHECK(error_msg != nullptr);
@@ -694,6 +592,15 @@ bool OatFileAssistant::RelocateOatFile(const std::string* input_file,
bool OatFileAssistant::GenerateOatFile(std::string* error_msg) {
CHECK(error_msg != nullptr);
+ // TODO: Currently we only know how to make a fully-compiled oat file.
+ // Perhaps we should support generating other kinds of oat files?
+ if ((target_compilation_type_mask_ & kFullCompilation) == 0) {
+ *error_msg = "Generation of oat file for dex location " + dex_location_
+ + " not attempted because full compilation was not specified"
+ + " as an acceptable target compilation type.";
+ return false;
+ }
+
Runtime* runtime = Runtime::Current();
if (!runtime->IsDex2OatEnabled()) {
*error_msg = "Generation of oat file for dex location " + dex_location_
@@ -861,21 +768,6 @@ std::string OatFileAssistant::DalvikCacheDirectory() {
return result;
}
-std::string OatFileAssistant::ProfileFileName() {
- if (package_name_ != nullptr) {
- return DalvikCacheDirectory() + std::string("profiles/") + package_name_;
- }
- return "";
-}
-
-std::string OatFileAssistant::OldProfileFileName() {
- std::string profile_name = ProfileFileName();
- if (profile_name.empty()) {
- return "";
- }
- return profile_name + "@old";
-}
-
std::string OatFileAssistant::ImageLocation() {
Runtime* runtime = Runtime::Current();
const std::vector<gc::space::ImageSpace*>& image_spaces =
@@ -1007,34 +899,6 @@ const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
return image_info_load_succeeded_ ? &cached_image_info_ : nullptr;
}
-ProfileFile* OatFileAssistant::GetProfile() {
- if (!profile_load_attempted_) {
- CHECK(package_name_ != nullptr)
- << "pakage_name_ is nullptr: "
- << "profile_load_attempted_ should have been true";
- profile_load_attempted_ = true;
- std::string profile_name = ProfileFileName();
- if (!profile_name.empty()) {
- profile_load_succeeded_ = cached_profile_.LoadFile(profile_name);
- }
- }
- return profile_load_succeeded_ ? &cached_profile_ : nullptr;
-}
-
-ProfileFile* OatFileAssistant::GetOldProfile() {
- if (!old_profile_load_attempted_) {
- CHECK(package_name_ != nullptr)
- << "pakage_name_ is nullptr: "
- << "old_profile_load_attempted_ should have been true";
- old_profile_load_attempted_ = true;
- std::string old_profile_name = OldProfileFileName();
- if (!old_profile_name.empty()) {
- old_profile_load_succeeded_ = cached_old_profile_.LoadFile(old_profile_name);
- }
- }
- return old_profile_load_succeeded_ ? &cached_old_profile_ : nullptr;
-}
-
gc::space::ImageSpace* OatFileAssistant::OpenImageSpace(const OatFile* oat_file) {
DCHECK(oat_file != nullptr);
std::string art_file = ArtFileName(oat_file);
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 7b45bca946..893aea2ab9 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -44,9 +44,6 @@ class ImageSpace;
// The oat file assistant is intended to be used with dex locations not on the
// boot class path. See the IsInBootClassPath method for a way to check if the
// dex location is in the boot class path.
-//
-// TODO: All the profiling related code is old and untested. It should either
-// be restored and tested, or removed.
class OatFileAssistant {
public:
enum DexOptNeeded {
@@ -73,8 +70,8 @@ class OatFileAssistant {
enum OatStatus {
// kOatOutOfDate - An oat file is said to be out of date if the file does
- // not exist, or is out of date with respect to the dex file or boot
- // image.
+ // not exist, is out of date with respect to the dex file or boot image,
+ // or does not meet the target compilation type.
kOatOutOfDate,
// kOatNeedsRelocation - An oat file is said to need relocation if the
@@ -88,6 +85,20 @@ class OatFileAssistant {
kOatUpToDate,
};
+ // Represents the different compilation types of oat files that OatFileAssitant
+ // and external GetDexOptNeeded callers care about.
+ // Note: these should be able to be used as part of a mask.
+ enum CompilationType {
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_FULL = 1
+ kFullCompilation = 1,
+
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_PROFILE_GUIDE = 2
+ kProfileGuideCompilation = 2,
+
+ // Matches Java: dalvik.system.DexFile.COMPILATION_TYPE_EXTRACT_ONLY = 4
+ kExtractOnly = 4,
+ };
+
// Constructs an OatFileAssistant object to assist the oat file
// corresponding to the given dex location with the target instruction set.
//
@@ -99,31 +110,28 @@ class OatFileAssistant {
// Note: Currently the dex_location must have an extension.
// TODO: Relax this restriction?
//
+ // The target compilation type specifies a set of CompilationTypes that
+ // should be considered up to date. An oat file compiled in a way not
+ // included in the set is considered out of date. For example, to consider
+ // otherwise up-to-date fully compiled and profile-guide compiled oat
+ // files as up to date, but to consider extract-only files as out of date,
+ // specify: (kFullCompilation | kProfileGuideCompilation).
+ //
// The isa should be either the 32 bit or 64 bit variant for the current
// device. For example, on an arm device, use arm or arm64. An oat file can
// be loaded executable only if the ISA matches the current runtime.
- OatFileAssistant(const char* dex_location, const InstructionSet isa,
+ OatFileAssistant(const char* dex_location,
+ int target_compilation_type_mask,
+ const InstructionSet isa,
bool load_executable);
// Constructs an OatFileAssistant, providing an explicit target oat_location
// to use instead of the standard oat location.
- OatFileAssistant(const char* dex_location, const char* oat_location,
- const InstructionSet isa, bool load_executable);
-
- // Constructs an OatFileAssistant, providing an additional package_name used
- // solely for the purpose of locating profile files.
- //
- // TODO: Why is the name of the profile file based on the package name and
- // not the dex location? If there is no technical reason the dex_location
- // can't be used, we should prefer that instead.
- OatFileAssistant(const char* dex_location, const InstructionSet isa,
- bool load_executable, const char* package_name);
-
- // Constructs an OatFileAssistant with user specified oat location and a
- // package name.
- OatFileAssistant(const char* dex_location, const char* oat_location,
- const InstructionSet isa, bool load_executable,
- const char* package_name);
+ OatFileAssistant(const char* dex_location,
+ const char* oat_location,
+ int target_compilation_type_mask,
+ const InstructionSet isa,
+ bool load_executable);
~OatFileAssistant();
@@ -233,28 +241,6 @@ class OatFileAssistant {
bool GivenOatFileNeedsRelocation(const OatFile& file);
bool GivenOatFileIsUpToDate(const OatFile& file);
- // Returns true if there is an accessible profile associated with the dex
- // location.
- // This returns false if profiling is disabled.
- bool ProfileExists();
-
- // The old profile is a file containing a previous snapshot of profiling
- // information associated with the dex file code. This is used to track how
- // the profiling information has changed over time.
- //
- // Returns true if there is an accessible old profile associated with the
- // dex location.
- // This returns false if profiling is disabled.
- bool OldProfileExists();
-
- // Returns true if there has been a significant change between the old
- // profile and the current profile.
- // This returns false if profiling is disabled.
- bool IsProfileChangeSignificant();
-
- // Copy the current profile to the old profile location.
- void CopyProfileFile();
-
// Generates the oat file by relocation from the named input file.
// This does not check the current status before attempting to relocate the
// oat file.
@@ -309,16 +295,6 @@ class OatFileAssistant {
// Returns an empty string if we can't get the dalvik cache directory path.
std::string DalvikCacheDirectory();
- // Constructs the filename for the profile file.
- // Returns an empty string if we do not have the necessary information to
- // construct the filename.
- std::string ProfileFileName();
-
- // Constructs the filename for the old profile file.
- // Returns an empty string if we do not have the necessary information to
- // construct the filename.
- std::string OldProfileFileName();
-
// Returns the current image location.
// Returns an empty string if the image location could not be retrieved.
//
@@ -364,35 +340,18 @@ class OatFileAssistant {
// The caller shouldn't clean up or free the returned pointer.
const ImageInfo* GetImageInfo();
- // Returns the loaded profile.
- // Loads the profile if needed. Returns null if the profile failed
- // to load.
- // The caller shouldn't clean up or free the returned pointer.
- ProfileFile* GetProfile();
-
- // Returns the loaded old profile.
- // Loads the old profile if needed. Returns null if the old profile
- // failed to load.
- // The caller shouldn't clean up or free the returned pointer.
- ProfileFile* GetOldProfile();
-
// To implement Lock(), we lock a dummy file where the oat file would go
// (adding ".flock" to the target file name) and retain the lock for the
// remaining lifetime of the OatFileAssistant object.
ScopedFlock flock_;
std::string dex_location_;
+ const int target_compilation_type_mask_;
// In a properly constructed OatFileAssistant object, isa_ should be either
// the 32 or 64 bit variant for the current device.
const InstructionSet isa_ = kNone;
- // The package name, used solely to find the profile file.
- // This may be null in a properly constructed object. In this case,
- // profile_load_attempted_ and old_profile_load_attempted_ will be true, and
- // profile_load_succeeded_ and old_profile_load_succeeded_ will be false.
- const char* package_name_ = nullptr;
-
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
@@ -451,18 +410,6 @@ class OatFileAssistant {
bool image_info_load_succeeded_ = false;
ImageInfo cached_image_info_;
- // Cached value of the profile file.
- // Use the GetProfile method rather than accessing these directly.
- bool profile_load_attempted_ = false;
- bool profile_load_succeeded_ = false;
- ProfileFile cached_profile_;
-
- // Cached value of the profile file.
- // Use the GetOldProfile method rather than accessing these directly.
- bool old_profile_load_attempted_ = false;
- bool old_profile_load_succeeded_ = false;
- ProfileFile cached_old_profile_;
-
// For debugging only.
// If this flag is set, the oat or odex file has been released to the user
// of the OatFileAssistant object and the OatFileAssistant object is in a
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 83d4457a1c..4541468cb3 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -260,7 +260,7 @@ class OatFileAssistantTest : public CommonRuntimeTest {
}
void GenerateExtractOnlyOdexForTest(const std::string& dex_location,
- const std::string& odex_location) {
+ const std::string& odex_location) {
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
args.push_back("--oat-file=" + odex_location);
@@ -277,7 +277,26 @@ class OatFileAssistantTest : public CommonRuntimeTest {
EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0u);
EXPECT_EQ(odex_file->GetOatHeader().GetImagePatchDelta(), 0);
-}
+ }
+
+ void GenerateProfileGuideOdexForTest(const std::string& dex_location,
+ const std::string& odex_location) {
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ ScratchFile profile_file;
+ args.push_back("--profile-file=" + profile_file.GetFilename());
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(
+ odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
+ false, dex_location.c_str(), &error_msg));
+ printf("error %s", error_msg.c_str());
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
+ }
private:
// Reserve memory around where the image will be loaded so other memory
@@ -344,7 +363,8 @@ class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest {
// Generate an oat file for the purposes of test, as opposed to testing
// generation of oat files.
static void GenerateOatForTest(const char* dex_location) {
- OatFileAssistant oat_file_assistant(dex_location, kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location,
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.GenerateOatFile(&error_msg)) << error_msg;
@@ -356,7 +376,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -379,7 +400,8 @@ TEST_F(OatFileAssistantTest, DexNoOat) {
TEST_F(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
@@ -400,7 +422,8 @@ TEST_F(OatFileAssistantTest, OatUpToDate) {
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -422,7 +445,8 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
@@ -448,7 +472,8 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
@@ -475,6 +500,7 @@ TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
// Verify we can load both dex files.
OatFileAssistant oat_file_assistant(dex_location.c_str(),
oat_location.c_str(),
+ OatFileAssistant::kFullCompilation,
kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -495,7 +521,8 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) {
GenerateOatForTest(dex_location.c_str());
Copy(GetDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
@@ -508,32 +535,6 @@ TEST_F(OatFileAssistantTest, OatOutOfDate) {
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
-// Case: We have a DEX file and an extract-only ODEX file out of date relative
-// to the DEX file.
-// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, ExtractOnlyOdexOutOfDate) {
- std::string dex_location = GetScratchDir() + "/ExtractOnlyOdexOutOfDate.jar";
- std::string odex_location = GetOdexDir() + "/ExtractOnlyOdexOutOfDate.odex";
-
- // We create a dex, generate an oat for it, then overwrite the dex with a
- // different dex to make the oat out of date.
- Copy(GetDexSrc1(), dex_location);
- GenerateExtractOnlyOdexForTest(dex_location.c_str(), odex_location.c_str());
- Copy(GetDexSrc2(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-
- EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_TRUE(oat_file_assistant.OdexFileExists());
- EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
- EXPECT_FALSE(oat_file_assistant.OatFileExists());
- EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
- EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
- EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
-}
-
// Case: We have a DEX file and an ODEX file, but no OAT file.
// Expect: The status is kPatchOatNeeded.
TEST_F(OatFileAssistantTest, DexOdexNoOat) {
@@ -545,7 +546,8 @@ TEST_F(OatFileAssistantTest, DexOdexNoOat) {
GenerateOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -578,7 +580,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexNoOat) {
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -633,7 +636,8 @@ TEST_F(OatFileAssistantTest, StrippedDexOdexOat) {
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -681,7 +685,8 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
Copy(GetStrippedDexSrc1(), dex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -724,7 +729,7 @@ TEST_F(OatFileAssistantTest, SelfRelocation) {
GenerateOdexForTest(dex_location, oat_location);
OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
+ oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -782,7 +787,7 @@ TEST_F(OatFileAssistantTest, OdexOatOverlap) {
// Verify things don't go bad.
OatFileAssistant oat_file_assistant(dex_location.c_str(),
- oat_location.c_str(), kRuntimeISA, true);
+ oat_location.c_str(), OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_EQ(OatFileAssistant::kPatchOatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -816,7 +821,8 @@ TEST_F(OatFileAssistantTest, DexPicOdexNoOat) {
GeneratePicOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -841,7 +847,9 @@ TEST_F(OatFileAssistantTest, DexExtractOnlyOdexNoOat) {
GenerateExtractOnlyOdexForTest(dex_location, odex_location);
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+ kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -864,7 +872,8 @@ TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
GenerateOatForTest(dex_location.c_str());
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -883,7 +892,8 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
GenerateOatForTest(dex_location.c_str());
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -903,7 +913,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) {
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
@@ -917,7 +928,8 @@ TEST_F(OatFileAssistantTest, LoadDexNoAlternateOat) {
EXPECT_TRUE(OS::FileExists(oat_location.c_str()));
// Verify it didn't create an oat in the default location.
- OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant ofm(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_FALSE(ofm.OatFileExists());
}
@@ -933,7 +945,8 @@ TEST_F(OatFileAssistantTest, LoadDexUnwriteableAlternateOat) {
Copy(GetDexSrc1(), dex_location);
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
@@ -948,7 +961,8 @@ TEST_F(OatFileAssistantTest, GenNoDex) {
std::string oat_location = GetScratchDir() + "/GenNoDex.oat";
OatFileAssistant oat_file_assistant(
- dex_location.c_str(), oat_location.c_str(), kRuntimeISA, true);
+ dex_location.c_str(), oat_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::string error_msg;
ASSERT_FALSE(oat_file_assistant.GenerateOatFile(&error_msg));
}
@@ -996,7 +1010,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
Copy(GetDexSrc1(), abs_dex_location);
std::string dex_location = MakePathRelative(abs_dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1013,7 +1028,8 @@ TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
TEST_F(OatFileAssistantTest, ShortDexLocation) {
std::string dex_location = "/xx";
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1037,7 +1053,8 @@ TEST_F(OatFileAssistantTest, LongDexExtension) {
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
@@ -1134,7 +1151,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadDexOdexNoOat) {
GenerateOdexForTest(dex_location, odex_location);
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1156,7 +1174,8 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
GenerateOdexForTest(dex_location, odex_location);
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
@@ -1184,6 +1203,45 @@ TEST(OatFileAssistantUtilsTest, DexFilenameToOdexFilename) {
"/foo/bar/baz_noext", kArm, &odex_file, &error_msg));
}
+// Case: We have a DEX file, extract-only ODEX, and fully compiled OAT.
+// Expect: The status depends on the target compilation type mask.
+TEST_F(OatFileAssistantTest, TargetCompilationType) {
+ std::string dex_location = GetScratchDir() + "/TargetCompilationType.jar";
+ std::string odex_location = GetOdexDir() + "/TargetCompilationType.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateExtractOnlyOdexForTest(dex_location, odex_location);
+ GenerateOatForTest(dex_location.c_str());
+
+ OatFileAssistant ofa_full(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_full.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_full.IsInBootClassPath());
+ EXPECT_TRUE(ofa_full.OdexFileIsOutOfDate());
+ EXPECT_TRUE(ofa_full.OatFileIsUpToDate());
+
+ OatFileAssistant ofa_extract(dex_location.c_str(),
+ OatFileAssistant::kExtractOnly, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_extract.IsInBootClassPath());
+ EXPECT_TRUE(ofa_extract.OdexFileIsUpToDate());
+ EXPECT_TRUE(ofa_extract.OatFileIsOutOfDate());
+
+ OatFileAssistant ofa_profile(dex_location.c_str(),
+ OatFileAssistant::kProfileGuideCompilation, kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, ofa_profile.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_profile.IsInBootClassPath());
+ EXPECT_TRUE(ofa_profile.OdexFileIsOutOfDate());
+ EXPECT_TRUE(ofa_profile.OatFileIsOutOfDate());
+
+ OatFileAssistant ofa_extract_full(dex_location.c_str(),
+ OatFileAssistant::kFullCompilation | OatFileAssistant::kExtractOnly,
+ kRuntimeISA, false);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, ofa_extract_full.GetDexOptNeeded());
+ EXPECT_FALSE(ofa_extract_full.IsInBootClassPath());
+ EXPECT_TRUE(ofa_extract_full.OdexFileIsUpToDate());
+ EXPECT_TRUE(ofa_extract_full.OatFileIsUpToDate());
+}
+
// Verify the dexopt status values from dalvik.system.DexFile
// match the OatFileAssistant::DexOptStatus values.
TEST_F(OatFileAssistantTest, DexOptStatusValues) {
@@ -1218,13 +1276,31 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) {
ASSERT_FALSE(self_patchoat_needed == nullptr);
EXPECT_EQ(self_patchoat_needed->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
EXPECT_EQ(OatFileAssistant::kSelfPatchOatNeeded, self_patchoat_needed->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_full = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_FULL", "I");
+ ASSERT_FALSE(compilation_type_full == nullptr);
+ EXPECT_EQ(compilation_type_full->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kFullCompilation, compilation_type_full->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_profile_guide = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_PROFILE_GUIDE", "I");
+ ASSERT_FALSE(compilation_type_profile_guide == nullptr);
+ EXPECT_EQ(compilation_type_profile_guide->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kProfileGuideCompilation,
+ compilation_type_profile_guide->GetInt(dexfile.Get()));
+
+ ArtField* compilation_type_extract_only = mirror::Class::FindStaticField(
+ soa.Self(), dexfile, "COMPILATION_TYPE_EXTRACT_ONLY", "I");
+ ASSERT_FALSE(compilation_type_extract_only == nullptr);
+ EXPECT_EQ(compilation_type_extract_only->GetTypeAsPrimitiveType(), Primitive::kPrimInt);
+ EXPECT_EQ(OatFileAssistant::kExtractOnly, compilation_type_extract_only->GetInt(dexfile.Get()));
}
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
// * Test using secondary isa
-// * Test with profiling info?
// * Test for status of oat while oat is being generated (how?)
// * Test case where 32 and 64 bit boot class paths differ,
// and we ask IsInBootClassPath for a class in exactly one of the 32 or
@@ -1233,5 +1309,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) {
// - Dex is stripped, don't have odex.
// - Oat file corrupted after status check, before reload unexecutable
// because it's unrelocated and no dex2oat
+// * Test unrelocated specific target compilation type can be relocated to
+// make it up to date.
} // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 9ae179fc5b..e57125bef1 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -307,8 +307,13 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
Thread* const self = Thread::Current();
Locks::mutator_lock_->AssertNotHeld(self);
Runtime* const runtime = Runtime::Current();
+
+ int target_compilation_type_mask = OatFileAssistant::kFullCompilation
+ | OatFileAssistant::kProfileGuideCompilation
+ | OatFileAssistant::kExtractOnly;
OatFileAssistant oat_file_assistant(dex_location,
oat_location,
+ target_compilation_type_mask,
kRuntimeISA,
!runtime->IsAotCompiler());
@@ -443,6 +448,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
+ std::string(dex_location));
}
}
+
+ // TODO(calin): Consider optimizing this knowing that is useless to record the
+ // use of fully compiled apks.
+ Runtime::Current()->NotifyDexLoaded(dex_location);
return dex_files;
}
diff --git a/runtime/quick/inline_method_analyser.cc b/runtime/quick/inline_method_analyser.cc
index 9b10f2e0b8..c7ccee2125 100644
--- a/runtime/quick/inline_method_analyser.cc
+++ b/runtime/quick/inline_method_analyser.cc
@@ -744,9 +744,12 @@ bool InlineMethodAnalyser::ComputeSpecialAccessorInfo(ArtMethod* method,
return false;
}
DCHECK_GE(field->GetOffset().Int32Value(), 0);
+ // Do not interleave function calls with bit field writes to placate valgrind. Bug: 27552451.
+ uint32_t field_offset = field->GetOffset().Uint32Value();
+ bool is_volatile = field->IsVolatile();
result->field_idx = field_idx;
- result->field_offset = field->GetOffset().Int32Value();
- result->is_volatile = field->IsVolatile();
+ result->field_offset = field_offset;
+ result->is_volatile = is_volatile ? 1u : 0u;
return true;
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index bbb79af1bb..e95f2c539f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -119,6 +119,7 @@
#include "os.h"
#include "parsed_options.h"
#include "profiler.h"
+#include "jit/profile_saver.h"
#include "quick/quick_method_frame_info.h"
#include "reflection.h"
#include "runtime_options.h"
@@ -1700,7 +1701,9 @@ void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) {
}
void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename) {
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir) {
if (jit_.get() == nullptr) {
// We are not JITing. Nothing to do.
return;
@@ -1723,7 +1726,18 @@ void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
}
profile_output_filename_ = profile_output_filename;
- jit_->StartProfileSaver(profile_output_filename, code_paths);
+ jit_->StartProfileSaver(profile_output_filename,
+ code_paths,
+ foreign_dex_profile_path,
+ app_dir);
+}
+
+void Runtime::NotifyDexLoaded(const std::string& dex_location) {
+ VLOG(profiler) << "Notify dex loaded: " << dex_location;
+ // We know that if the ProfileSaver is started then we can record profile information.
+ if (ProfileSaver::IsStarted()) {
+ ProfileSaver::NotifyDexUse(dex_location);
+ }
}
// Transaction support.
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 83e77d2372..8e99f800e0 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -467,7 +467,10 @@ class Runtime {
}
void RegisterAppInfo(const std::vector<std::string>& code_paths,
- const std::string& profile_output_filename);
+ const std::string& profile_output_filename,
+ const std::string& foreign_dex_profile_path,
+ const std::string& app_dir);
+ void NotifyDexLoaded(const std::string& dex_location);
// Transaction support.
bool IsActiveTransaction() const {
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 8237b06a56..bc963c5b8c 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -36,6 +36,7 @@ namespace art {
static constexpr bool kDumpHeapObjectOnSigsevg = false;
static constexpr bool kUseSigRTTimeout = true;
+static constexpr bool kDumpNativeStackOnTimeout = true;
struct Backtrace {
public:
@@ -350,7 +351,9 @@ void HandleUnexpectedSignal(int signal_number, siginfo_t* info, void* raw_contex
if (runtime != nullptr) {
if (IsTimeoutSignal(signal_number)) {
// Special timeout signal. Try to dump all threads.
- runtime->GetThreadList()->DumpForSigQuit(LOG(INTERNAL_FATAL));
+ // Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
+ // are of value here.
+ runtime->GetThreadList()->Dump(LOG(INTERNAL_FATAL), kDumpNativeStackOnTimeout);
}
gc::Heap* heap = runtime->GetHeap();
LOG(INTERNAL_FATAL) << "Fault message: " << runtime->GetFaultMessage();
diff --git a/test/004-checker-UnsafeTest18/src/Main.java b/test/004-checker-UnsafeTest18/src/Main.java
index bb6de2ef28..bb020b9b9f 100644
--- a/test/004-checker-UnsafeTest18/src/Main.java
+++ b/test/004-checker-UnsafeTest18/src/Main.java
@@ -87,18 +87,36 @@ public class Main {
/// CHECK-START: void Main.load() intrinsics_recognition (after)
/// CHECK-DAG: InvokeVirtual intrinsic:UnsafeLoadFence
+ //
+ /// CHECK-START: void Main.load() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeLoadFence
+ //
+ /// CHECK-START: void Main.load() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:LoadAny
private static void load() {
unsafe.loadFence();
}
/// CHECK-START: void Main.store() intrinsics_recognition (after)
/// CHECK-DAG: InvokeVirtual intrinsic:UnsafeStoreFence
+ //
+ /// CHECK-START: void Main.store() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeStoreFence
+ //
+ /// CHECK-START: void Main.store() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:AnyStore
private static void store() {
unsafe.storeFence();
}
/// CHECK-START: void Main.full() intrinsics_recognition (after)
/// CHECK-DAG: InvokeVirtual intrinsic:UnsafeFullFence
+ //
+ /// CHECK-START: void Main.full() instruction_simplifier (after)
+ /// CHECK-NOT: InvokeVirtual intrinsic:UnsafeFullFence
+ //
+ /// CHECK-START: void Main.full() instruction_simplifier (after)
+ /// CHECK-DAG: MemoryBarrier kind:AnyAny
private static void full() {
unsafe.fullFence();
}
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index bcb697a396..15683b0b1e 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -181,6 +181,7 @@ public class Main {
Class intHolder = loader.loadClass("IntHolder");
Method loadLibrary = intHolder.getDeclaredMethod("loadLibrary", String.class);
loadLibrary.invoke(intHolder, nativeLibraryName);
+ waitForCompilation(intHolder);
return new WeakReference(loader);
}
diff --git a/test/145-alloc-tracking-stress/src/Main.java b/test/145-alloc-tracking-stress/src/Main.java
index 752fdd9135..418690a2a6 100644
--- a/test/145-alloc-tracking-stress/src/Main.java
+++ b/test/145-alloc-tracking-stress/src/Main.java
@@ -1,5 +1,4 @@
/*
-
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/test/577-profile-foreign-dex/expected.txt b/test/577-profile-foreign-dex/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/577-profile-foreign-dex/expected.txt
diff --git a/test/577-profile-foreign-dex/info.txt b/test/577-profile-foreign-dex/info.txt
new file mode 100644
index 0000000000..090db3fdc6
--- /dev/null
+++ b/test/577-profile-foreign-dex/info.txt
@@ -0,0 +1 @@
+Check that we record the use of foreign dex files when profiles are enabled.
diff --git a/test/577-profile-foreign-dex/run b/test/577-profile-foreign-dex/run
new file mode 100644
index 0000000000..ad57d14c60
--- /dev/null
+++ b/test/577-profile-foreign-dex/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+exec ${RUN} \
+ --runtime-option -Xjitsaveprofilinginfo \
+ --runtime-option -Xusejit:true \
+ "${@}"
diff --git a/test/577-profile-foreign-dex/src-ex/OtherDex.java b/test/577-profile-foreign-dex/src-ex/OtherDex.java
new file mode 100644
index 0000000000..cba73b3094
--- /dev/null
+++ b/test/577-profile-foreign-dex/src-ex/OtherDex.java
@@ -0,0 +1,17 @@
+/*
+ * 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 OtherDex {
+}
diff --git a/test/577-profile-foreign-dex/src/Main.java b/test/577-profile-foreign-dex/src/Main.java
new file mode 100644
index 0000000000..0cd85b58e8
--- /dev/null
+++ b/test/577-profile-foreign-dex/src/Main.java
@@ -0,0 +1,175 @@
+/*
+ * 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.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+public class Main {
+
+ private static final String PROFILE_NAME = "primary.prof";
+ private static final String APP_DIR_PREFIX = "app_dir_";
+ private static final String FOREIGN_DEX_PROFILE_DIR = "foreign-dex";
+ private static final String TEMP_FILE_NAME_PREFIX = "dummy";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+
+ public static void main(String[] args) throws Exception {
+ File tmpFile = null;
+ File appDir = null;
+ File profileFile = null;
+ File foreignDexProfileDir = null;
+
+ try {
+ // Create the necessary files layout.
+ tmpFile = createTempFile();
+ appDir = new File(tmpFile.getParent(), APP_DIR_PREFIX + tmpFile.getName());
+ appDir.mkdir();
+ foreignDexProfileDir = new File(tmpFile.getParent(), FOREIGN_DEX_PROFILE_DIR);
+ foreignDexProfileDir.mkdir();
+ profileFile = createTempFile();
+
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+
+ // Register the app with the runtime
+ VMRuntime.registerAppInfo(profileFile.getPath(), appDir.getPath(),
+ new String[] { codePath }, foreignDexProfileDir.getPath());
+
+ testMarkerForForeignDex(foreignDexProfileDir);
+ testMarkerForCodePath(foreignDexProfileDir);
+ testMarkerForApplicationDexFile(foreignDexProfileDir, appDir);
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ if (profileFile != null) {
+ profileFile.delete();
+ }
+ if (foreignDexProfileDir != null) {
+ foreignDexProfileDir.delete();
+ }
+ if (appDir != null) {
+ appDir.delete();
+ }
+ }
+ }
+
+ // Verify we actually create a marker on disk for foreign dex files.
+ private static void testMarkerForForeignDex(File foreignDexProfileDir) throws Exception {
+ String foreignDex = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar";
+ loadDexFile(foreignDex);
+ checkMarker(foreignDexProfileDir, foreignDex, /* exists */ true);
+ }
+
+ // Verify we do not create a marker on disk for dex files path of the code path.
+ private static void testMarkerForCodePath(File foreignDexProfileDir) throws Exception {
+ String codePath = System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex.jar";
+ loadDexFile(codePath);
+ checkMarker(foreignDexProfileDir, codePath, /* exists */ false);
+ }
+
+ private static void testMarkerForApplicationDexFile(File foreignDexProfileDir, File appDir)
+ throws Exception {
+ // Copy the -ex jar to the application directory and load it from there.
+ // This will record duplicate class conflicts but we don't care for this use case.
+ File foreignDex = new File(System.getenv("DEX_LOCATION") + "/577-profile-foreign-dex-ex.jar");
+ File appDex = new File(appDir, "appDex.jar");
+ try {
+ copyFile(foreignDex, appDex);
+
+ loadDexFile(appDex.getAbsolutePath());
+ checkMarker(foreignDexProfileDir, appDex.getAbsolutePath(), /* exists */ false);
+ } finally {
+ if (appDex != null) {
+ appDex.delete();
+ }
+ }
+ }
+
+ private static void checkMarker(File foreignDexProfileDir, String dexFile, boolean exists) {
+ File marker = new File(foreignDexProfileDir, dexFile.replace('/', '@'));
+ boolean result_ok = exists ? marker.exists() : !marker.exists();
+ if (!result_ok) {
+ throw new RuntimeException("Marker test failed for:" + marker.getPath());
+ }
+ }
+
+ private static void loadDexFile(String dexFile) throws Exception {
+ Class pathClassLoader = Class.forName("dalvik.system.PathClassLoader");
+ if (pathClassLoader == null) {
+ throw new RuntimeException("Couldn't find path class loader class");
+ }
+ Constructor constructor =
+ pathClassLoader.getDeclaredConstructor(String.class, ClassLoader.class);
+ constructor.newInstance(
+ dexFile, ClassLoader.getSystemClassLoader());
+ }
+
+ private static class VMRuntime {
+ private static final Method registerAppInfoMethod;
+ static {
+ try {
+ Class c = Class.forName("dalvik.system.VMRuntime");
+ registerAppInfoMethod = c.getDeclaredMethod("registerAppInfo",
+ String.class, String.class, String[].class, String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void registerAppInfo(String pkgName, String appDir,
+ String[] codePath, String foreignDexProfileDir) throws Exception {
+ registerAppInfoMethod.invoke(null, pkgName, appDir, codePath, foreignDexProfileDir);
+ }
+ }
+
+ private static void copyFile(File fromFile, File toFile) throws Exception {
+ FileInputStream in = new FileInputStream(fromFile);
+ FileOutputStream out = new FileOutputStream(toFile);
+ try {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ out.flush();
+ try {
+ out.getFD().sync();
+ } catch (IOException e) {
+ }
+ out.close();
+ in.close();
+ }
+ }
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index c4f0171f0d..7036bdcaf5 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -567,7 +567,9 @@ TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := \
537-checker-arraycopy
# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler).
-TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS :=
+# 145: Test sometimes times out in read barrier configuration (b/27467554).
+TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := \
+ 145-alloc-tracking-stress
ifeq ($(ART_USE_READ_BARRIER),true)
ifneq (,$(filter interpreter,$(COMPILER_TYPES)))