diff options
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))) |