diff options
40 files changed, 1206 insertions, 800 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 1e074305d3..086f298df6 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -84,7 +84,7 @@ else ART_TARGET_CLANG := false endif -ifeq ($(TARGET_ARCH),mips) +ifeq ($(TARGET_ARCH)|$(ART_TARGET_CLANG),mips|true) # b/18807290, Clang generated mips assembly code for array.cc # cannot be compiled by gas. # b/18789639, Clang assembler cannot compile inlined assembly code in diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index b05939156f..52a516cc5a 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -929,6 +929,10 @@ RegStorage ArmMir2Lir::InToRegStorageArmMapper::GetNextReg(ShortyArg arg) { } } else { if (cur_core_reg_ < coreArgMappingToPhysicalRegSize) { + if (!kArm32QuickCodeUseSoftFloat && arg.IsWide() && cur_core_reg_ == 0) { + // Skip r1, and use r2-r3 for the register pair. + cur_core_reg_++; + } result = coreArgMappingToPhysicalReg[cur_core_reg_++]; if (arg.IsWide() && cur_core_reg_ < coreArgMappingToPhysicalRegSize) { result = RegStorage::MakeRegPair(result, coreArgMappingToPhysicalReg[cur_core_reg_++]); diff --git a/compiler/dex/quick/mips/assemble_mips.cc b/compiler/dex/quick/mips/assemble_mips.cc index 0d1d9bf7a9..c48833b274 100644 --- a/compiler/dex/quick/mips/assemble_mips.cc +++ b/compiler/dex/quick/mips/assemble_mips.cc @@ -434,7 +434,7 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { * anchor: * ori rAT, rAT, ((target-anchor) & 0xffff) * addu rAT, rAT, rRA - * jr rAT + * jalr rZERO, rAT * hop: * * Orig unconditional branch @@ -448,7 +448,7 @@ const MipsEncodingMap MipsMir2Lir::EncodingMap[kMipsLast] = { * anchor: * ori rAT, rAT, ((target-anchor) & 0xffff) * addu rAT, rAT, rRA - * jr rAT + * jalr rZERO, rAT * * * NOTE: An out-of-range bal isn't supported because it should @@ -497,8 +497,8 @@ void MipsMir2Lir::ConvertShortToLongBranch(LIR* lir) { InsertLIRBefore(lir, delta_lo); LIR* addu = RawLIR(dalvik_offset, kMipsAddu, rAT, rAT, rRA); InsertLIRBefore(lir, addu); - LIR* jr = RawLIR(dalvik_offset, kMipsJr, rAT); - InsertLIRBefore(lir, jr); + LIR* jalr = RawLIR(dalvik_offset, kMipsJalr, rZERO, rAT); + InsertLIRBefore(lir, jalr); if (!unconditional) { InsertLIRBefore(lir, hop_target); } diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 3bb81bf28e..51a8c981fb 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -58,7 +58,7 @@ bool MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& s * bne r_val, r_key, loop * lw r_disp, -4(r_base) * addu rRA, r_disp - * jr rRA + * jalr rZERO, rRA * done: * */ @@ -136,7 +136,7 @@ void MipsMir2Lir::GenLargeSparseSwitch(MIR* mir, DexOffset table_offset, RegLoca * bound check -> done * lw r_disp, [rRA, r_val] * addu rRA, r_disp - * jr rRA + * jalr rZERO, rRA * done: */ void MipsMir2Lir::GenLargePackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src) { diff --git a/compiler/dex/quick/mips/utility_mips.cc b/compiler/dex/quick/mips/utility_mips.cc index 18f1cde353..adb9270693 100644 --- a/compiler/dex/quick/mips/utility_mips.cc +++ b/compiler/dex/quick/mips/utility_mips.cc @@ -125,7 +125,7 @@ LIR* MipsMir2Lir::OpReg(OpKind op, RegStorage r_dest_src) { opcode = kMipsJalr; break; case kOpBx: - return NewLIR1(kMipsJr, r_dest_src.GetReg()); + return NewLIR2(kMipsJalr, rZERO, r_dest_src.GetReg()); break; default: LOG(FATAL) << "Bad case in OpReg"; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index e37180768a..9985d66469 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -339,8 +339,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, const InstructionSetFeatures* instruction_set_features, bool image, std::set<std::string>* image_classes, std::set<std::string>* compiled_classes, size_t thread_count, - bool dump_stats, bool dump_passes, - const std::string& dump_cfg_file_name, CumulativeLogger* timer, + bool dump_stats, bool dump_passes, CumulativeLogger* timer, int swap_fd, const std::string& profile_file) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())), @@ -362,7 +361,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, stats_(new AOTCompilationStats), dump_stats_(dump_stats), dump_passes_(dump_passes), - dump_cfg_file_name_(dump_cfg_file_name), timings_logger_(timer), compiler_context_(nullptr), support_boot_image_fixup_(instruction_set != kMips), diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 11b4329e32..7ddc32cdd8 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -97,7 +97,6 @@ class CompilerDriver { bool image, std::set<std::string>* image_classes, std::set<std::string>* compiled_classes, size_t thread_count, bool dump_stats, bool dump_passes, - const std::string& dump_cfg_file_name, CumulativeLogger* timer, int swap_fd, const std::string& profile_file); @@ -372,10 +371,6 @@ class CompilerDriver { return dump_passes_; } - const std::string& GetDumpCfgFileName() const { - return dump_cfg_file_name_; - } - CumulativeLogger* GetTimingsLogger() const { return timings_logger_; } @@ -547,7 +542,6 @@ class CompilerDriver { bool dump_stats_; const bool dump_passes_; - const std::string& dump_cfg_file_name_; CumulativeLogger* const timings_logger_; diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc index fd207150f7..669c3bb716 100644 --- a/compiler/jni/quick/arm/calling_convention_arm.cc +++ b/compiler/jni/quick/arm/calling_convention_arm.cc @@ -168,6 +168,13 @@ const ManagedRegisterEntrySpills& ArmManagedRuntimeCallingConvention::EntrySpill } else { // FIXME: Pointer this returns as both reference and long. if (IsCurrentParamALong() && !IsCurrentParamAReference()) { // Long. + if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) { + // Skip R1, and use R2_R3 if the long is the first parameter. + if (gpr_index == 1) { + gpr_index++; + } + } + // If it spans register and memory, we must use the value in memory. if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) { entry_spills_.push_back( diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 1cc2dcc9b8..1862061bcf 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -49,6 +49,9 @@ static constexpr SRegister kRuntimeParameterFpuRegisters[] = { S0, S1, S2, S3 }; static constexpr size_t kRuntimeParameterFpuRegistersLength = arraysize(kRuntimeParameterFpuRegisters); +static constexpr DRegister DTMP = D7; +static constexpr SRegister STMP = S14; + class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> { public: InvokeRuntimeCallingConvention() @@ -472,6 +475,11 @@ void CodeGeneratorARM::SetupBlockedRegisters() const { blocked_core_registers_[R10] = true; blocked_core_registers_[R11] = true; + // Don't allocate our temporary double register. + blocked_fpu_registers_[STMP] = true; + blocked_fpu_registers_[STMP + 1] = true; + DCHECK_EQ(FromLowSToD(STMP), DTMP); + blocked_fpu_registers_[S16] = true; blocked_fpu_registers_[S17] = true; blocked_fpu_registers_[S18] = true; @@ -590,9 +598,17 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type gp_index_ += 2; stack_index_ += 2; if (index + 1 < calling_convention.GetNumberOfRegisters()) { - ArmManagedRegister pair = ArmManagedRegister::FromRegisterPair( - calling_convention.GetRegisterPairAt(index)); - return Location::RegisterPairLocation(pair.AsRegisterPairLow(), pair.AsRegisterPairHigh()); + if (calling_convention.GetRegisterAt(index) == R1) { + // Skip R1, and use R2_R3 instead. + gp_index_++; + index++; + } + } + if (index + 1 < calling_convention.GetNumberOfRegisters()) { + DCHECK_EQ(calling_convention.GetRegisterAt(index) + 1, + calling_convention.GetRegisterAt(index + 1)); + return Location::RegisterPairLocation(calling_convention.GetRegisterAt(index), + calling_convention.GetRegisterAt(index + 1)); } else { return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index)); } @@ -617,6 +633,9 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) { uint32_t index = double_index_; double_index_ += 2; + DCHECK_EQ(calling_convention.GetFpuRegisterAt(index) + 1, + calling_convention.GetFpuRegisterAt(index + 1)); + DCHECK_EQ(calling_convention.GetFpuRegisterAt(index) & 1, 0); return Location::FpuRegisterPairLocation( calling_convention.GetFpuRegisterAt(index), calling_convention.GetFpuRegisterAt(index + 1)); @@ -3364,9 +3383,9 @@ void ParallelMoveResolverARM::EmitSwap(size_t index) { } else if (source.IsStackSlot() && destination.IsStackSlot()) { Exchange(source.GetStackIndex(), destination.GetStackIndex()); } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { - __ vmovrs(IP, source.AsFpuRegister<SRegister>()); + __ vmovs(STMP, source.AsFpuRegister<SRegister>()); __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>()); - __ vmovsr(destination.AsFpuRegister<SRegister>(), IP); + __ vmovs(destination.AsFpuRegister<SRegister>(), STMP); } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>() : destination.AsFpuRegister<SRegister>(); @@ -3374,11 +3393,33 @@ void ParallelMoveResolverARM::EmitSwap(size_t index) { ? destination.GetStackIndex() : source.GetStackIndex(); - __ vmovrs(IP, reg); + __ vmovs(STMP, reg); __ LoadSFromOffset(reg, SP, mem); - __ StoreToOffset(kStoreWord, IP, SP, mem); + __ StoreSToOffset(STMP, SP, mem); + } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) { + __ vmovd(DTMP, FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())); + __ vmovd(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()), + FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>())); + __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), DTMP); + } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) { + DRegister reg = source.IsFpuRegisterPair() + ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()) + : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()); + int mem = source.IsFpuRegisterPair() + ? destination.GetStackIndex() + : source.GetStackIndex(); + + __ vmovd(DTMP, reg); + __ LoadDFromOffset(reg, SP, mem); + __ StoreDToOffset(DTMP, SP, mem); + } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { + // TODO: We could use DTMP and ask for a pair scratch register (float or core). + // This would save four instructions if two scratch registers are available, and + // two instructions if not. + Exchange(source.GetStackIndex(), destination.GetStackIndex()); + Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize)); } else { - LOG(FATAL) << "Unimplemented"; + LOG(FATAL) << "Unimplemented" << source << " <-> " << destination; } } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index c1b4eda3a4..8b29b159ab 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -33,7 +33,6 @@ class SlowPathCodeARM; static constexpr size_t kArmWordSize = kArmPointerSize; static constexpr Register kParameterCoreRegisters[] = { R1, R2, R3 }; -static constexpr RegisterPair kParameterCorePairRegisters[] = { R1_R2, R2_R3 }; static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters); static constexpr SRegister kParameterFpuRegisters[] = { S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15 }; @@ -47,11 +46,6 @@ class InvokeDexCallingConvention : public CallingConvention<Register, SRegister> kParameterFpuRegisters, kParameterFpuRegistersLength) {} - RegisterPair GetRegisterPairAt(size_t argument_index) { - DCHECK_LT(argument_index + 1, GetNumberOfRegisters()); - return kParameterCorePairRegisters[argument_index]; - } - private: DISALLOW_COPY_AND_ASSIGN(InvokeDexCallingConvention); }; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index eaecbb04ae..692d452f54 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -149,12 +149,11 @@ void OptimizingCompiler::Init() { // Enable C1visualizer output. Must be done in Init() because the compiler // driver is not fully initialized when passed to the compiler's constructor. CompilerDriver* driver = GetCompilerDriver(); - const std::string cfg_file_name = driver->GetDumpCfgFileName(); - if (!cfg_file_name.empty()) { + if (driver->GetDumpPasses()) { CHECK_EQ(driver->GetThreadCount(), 1U) << "Graph visualizer requires the compiler to run single-threaded. " << "Invoke the compiler with '-j1'."; - visualizer_output_.reset(new std::ofstream(cfg_file_name)); + visualizer_output_.reset(new std::ofstream("art.cfg")); } } diff --git a/compiler/utils/mips/assembler_mips.cc b/compiler/utils/mips/assembler_mips.cc index 8001dcd2fb..b5437b0eda 100644 --- a/compiler/utils/mips/assembler_mips.cc +++ b/compiler/utils/mips/assembler_mips.cc @@ -332,7 +332,7 @@ void MipsAssembler::Jal(uint32_t address) { } void MipsAssembler::Jr(Register rs) { - EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x08); + EmitR(0, rs, static_cast<Register>(0), static_cast<Register>(0), 0, 0x09); // Jalr zero, rs Nop(); } @@ -420,7 +420,7 @@ void MipsAssembler::Nop() { } void MipsAssembler::Move(Register rt, Register rs) { - EmitI(0x8, rs, rt, 0); + EmitI(0x9, rs, rt, 0); // Addiu } void MipsAssembler::Clear(Register rt) { @@ -447,11 +447,11 @@ void MipsAssembler::Rem(Register rd, Register rs, Register rt) { } void MipsAssembler::AddConstant(Register rt, Register rs, int32_t value) { - Addi(rt, rs, value); + Addiu(rt, rs, value); } void MipsAssembler::LoadImmediate(Register rt, int32_t value) { - Addi(rt, ZERO, value); + Addiu(rt, ZERO, value); } void MipsAssembler::EmitLoad(ManagedRegister m_dst, Register src_register, int32_t src_offset, diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8e564d1851..4b4675b57c 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -34,6 +34,7 @@ #include <cutils/trace.h> #include "arch/instruction_set_features.h" +#include "arch/mips/instruction_set_features_mips.h" #include "base/dumpable.h" #include "base/stl_util.h" #include "base/stringpiece.h" @@ -659,8 +660,6 @@ class Dex2Oat FINAL { dump_timing_ = true; } else if (option == "--dump-passes") { dump_passes_ = true; - } else if (option.starts_with("--dump-cfg=")) { - dump_cfg_file_name_ = option.substr(strlen("--dump-cfg=")).data(); } else if (option == "--dump-stats") { dump_stats_ = true; } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") { @@ -854,7 +853,13 @@ class Dex2Oat FINAL { } if (compiler_filter_string == nullptr) { - if (instruction_set_ == kMips64) { + if (instruction_set_ == kMips && + reinterpret_cast<const MipsInstructionSetFeatures*>(instruction_set_features_.get())-> + IsR6()) { + // For R6, only interpreter mode is working. + // TODO: fix compiler for Mips32r6. + compiler_filter_string = "interpret-only"; + } else if (instruction_set_ == kMips64) { // TODO: fix compiler for Mips64. compiler_filter_string = "interpret-only"; } else if (image_) { @@ -1213,7 +1218,6 @@ class Dex2Oat FINAL { thread_count_, dump_stats_, dump_passes_, - dump_cfg_file_name_, compiler_phases_timings_.get(), swap_fd_, profile_file_)); @@ -1664,7 +1668,6 @@ class Dex2Oat FINAL { bool dump_passes_; bool dump_timing_; bool dump_slow_timing_; - std::string dump_cfg_file_name_; std::string swap_file_name_; int swap_fd_; std::string profile_file_; // Profile file to use diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 97c06f178d..7442c705fa 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -138,7 +138,9 @@ static const MipsInstruction gMipsInstructions[] = { { kITypeMask, 41u << kOpcodeShift, "sh", "TO", }, { kITypeMask, 43u << kOpcodeShift, "sw", "TO", }, { kITypeMask, 49u << kOpcodeShift, "lwc1", "tO", }, + { kITypeMask, 53u << kOpcodeShift, "ldc1", "tO", }, { kITypeMask, 57u << kOpcodeShift, "swc1", "tO", }, + { kITypeMask, 61u << kOpcodeShift, "sdc1", "tO", }, // Floating point. { kFpMask, kCop1 | 0, "add", "fdst" }, diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b6ec223680..28f966826b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -48,7 +48,7 @@ namespace art { -static InstructionSet ElfISAToInstructionSet(Elf32_Word isa) { +static InstructionSet ElfISAToInstructionSet(Elf32_Word isa, Elf32_Word e_flags) { switch (isa) { case EM_ARM: return kArm; @@ -59,7 +59,12 @@ static InstructionSet ElfISAToInstructionSet(Elf32_Word isa) { case EM_X86_64: return kX86_64; case EM_MIPS: - return kMips; + if (((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2) || + ((e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6)) { + return kMips; + } else { + return kNone; + } default: return kNone; } @@ -212,7 +217,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d LOG(ERROR) << "unable to read elf header"; return false; } - isa = ElfISAToInstructionSet(elf_hdr.e_machine); + isa = ElfISAToInstructionSet(elf_hdr.e_machine, elf_hdr.e_flags); } const char* isa_name = GetInstructionSetString(isa); std::string image_filename; diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc index e21e6c1a2e..a3acd7e10a 100644 --- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc +++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc @@ -75,7 +75,14 @@ static void quick_invoke_reg_setup(mirror::ArtMethod* method, uint32_t* args, ui } break; case 'J': + if (gpr_index == 1 && !kArm32QuickCodeUseSoftFloat) { + // Don't use r1-r2 as a register pair, move to r2-r3 instead. + gpr_index++; + } if (gpr_index < arraysize(core_reg_args)) { + // Note that we don't need to do this if two registers are not available + // when !kArm32QuickCodeUseSoftFloat. We do it anyway to leave this + // code simple. core_reg_args[gpr_index++] = args[arg_index]; } ++arg_index; diff --git a/runtime/arch/memcmp16.h b/runtime/arch/memcmp16.h index 4b9fb8eff6..c449a14b54 100644 --- a/runtime/arch/memcmp16.h +++ b/runtime/arch/memcmp16.h @@ -30,7 +30,7 @@ // // In both cases, MemCmp16 is declared. -#if defined(__aarch64__) || defined(__arm__) || defined(__mips) || defined(__i386__) || defined(__x86_64__) +#if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || defined(__i386__) || defined(__x86_64__) extern "C" uint32_t __memcmp16(const uint16_t* s0, const uint16_t* s1, size_t count); #define MemCmp16 __memcmp16 diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc index 11be2a8bc2..00ab613b94 100644 --- a/runtime/arch/mips/instruction_set_features_mips.cc +++ b/runtime/arch/mips/instruction_set_features_mips.cc @@ -25,52 +25,82 @@ namespace art { const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromVariant( - const std::string& variant ATTRIBUTE_UNUSED, std::string* error_msg ATTRIBUTE_UNUSED) { - if (variant != "default") { - std::ostringstream os; - LOG(WARNING) << "Unexpected CPU variant for Mips using defaults: " << variant; - } + const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) { + bool smp = true; // Conservative default. bool fpu_32bit = true; - bool mips_isa_gte2 = true; - return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2); + bool mips_isa_gte2 = false; + bool r6 = false; + + // Override defaults based on variant string. + // Only care if it is R1, R2 or R6 and we assume all CPUs will have a FP unit. + constexpr const char* kMips32Prefix = "mips32r"; + const size_t kPrefixLength = strlen(kMips32Prefix); + if (variant.compare(0, kPrefixLength, kMips32Prefix, kPrefixLength) == 0 && + variant.size() > kPrefixLength) { + if (variant[kPrefixLength] >= '6') { + fpu_32bit = false; + r6 = true; + } + if (variant[kPrefixLength] >= '2') { + mips_isa_gte2 = true; + } + } else if (variant == "default") { + // Default variant is: smp = true, has fpu, is gte2, is not r6. This is the traditional + // setting. + mips_isa_gte2 = true; + } else { + LOG(WARNING) << "Unexpected CPU variant for Mips32 using defaults: " << variant; + } + + return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6); } const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromBitmap(uint32_t bitmap) { bool smp = (bitmap & kSmpBitfield) != 0; bool fpu_32bit = (bitmap & kFpu32Bitfield) != 0; bool mips_isa_gte2 = (bitmap & kIsaRevGte2Bitfield) != 0; - return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2); + bool r6 = (bitmap & kR6) != 0; + return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6); } const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCppDefines() { + // Assume conservative defaults. const bool smp = true; + bool fpu_32bit = true; + bool mips_isa_gte2 = false; + bool r6 = false; - // TODO: here we assume the FPU is always 32-bit. - const bool fpu_32bit = true; + // Override defaults based on compiler flags. +#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6) + mips_isa_gte2 = true; +#endif -#if __mips_isa_rev >= 2 - const bool mips_isa_gte2 = true; -#else - const bool mips_isa_gte2 = false; +#if defined(_MIPS_ARCH_MIPS32R6) + r6 = true; + fpu_32bit = false; #endif - return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2); + return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6); } const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCpuInfo() { // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. + // Assume conservative defaults. bool smp = false; + bool fpu_32bit = true; + bool mips_isa_gte2 = false; + bool r6 = false; - // TODO: here we assume the FPU is always 32-bit. - const bool fpu_32bit = true; + // Override defaults based on compiler flags. +#if (_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS32R5) || defined(_MIPS_ARCH_MIPS32R6) + mips_isa_gte2 = true; +#endif - // TODO: here we assume all MIPS processors are >= v2. -#if __mips_isa_rev >= 2 - const bool mips_isa_gte2 = true; -#else - const bool mips_isa_gte2 = false; +#if defined(_MIPS_ARCH_MIPS32R6) + r6 = true; + fpu_32bit = false; #endif std::ifstream in("/proc/cpuinfo"); @@ -89,7 +119,7 @@ const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromCpuInfo() { } else { LOG(ERROR) << "Failed to open /proc/cpuinfo"; } - return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2); + return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6); } const MipsInstructionSetFeatures* MipsInstructionSetFeatures::FromHwcap() { @@ -109,13 +139,15 @@ bool MipsInstructionSetFeatures::Equals(const InstructionSetFeatures* other) con const MipsInstructionSetFeatures* other_as_mips = other->AsMipsInstructionSetFeatures(); return (IsSmp() == other->IsSmp()) && (fpu_32bit_ == other_as_mips->fpu_32bit_) && - (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_); + (mips_isa_gte2_ == other_as_mips->mips_isa_gte2_) && + (r6_ == other_as_mips->r6_); } uint32_t MipsInstructionSetFeatures::AsBitmap() const { return (IsSmp() ? kSmpBitfield : 0) | (fpu_32bit_ ? kFpu32Bitfield : 0) | - (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0); + (mips_isa_gte2_ ? kIsaRevGte2Bitfield : 0) | + (r6_ ? kR6 : 0); } std::string MipsInstructionSetFeatures::GetFeatureString() const { @@ -135,6 +167,9 @@ std::string MipsInstructionSetFeatures::GetFeatureString() const { } else { result += ",-mips2"; } + if (r6_) { + result += ",r6"; + } // Suppress non-r6. return result; } @@ -142,6 +177,7 @@ const InstructionSetFeatures* MipsInstructionSetFeatures::AddFeaturesFromSplitSt const bool smp, const std::vector<std::string>& features, std::string* error_msg) const { bool fpu_32bit = fpu_32bit_; bool mips_isa_gte2 = mips_isa_gte2_; + bool r6 = r6_; for (auto i = features.begin(); i != features.end(); i++) { std::string feature = Trim(*i); if (feature == "fpu32") { @@ -152,12 +188,16 @@ const InstructionSetFeatures* MipsInstructionSetFeatures::AddFeaturesFromSplitSt mips_isa_gte2 = true; } else if (feature == "-mips2") { mips_isa_gte2 = false; + } else if (feature == "r6") { + r6 = true; + } else if (feature == "-r6") { + r6 = false; } else { *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); return nullptr; } } - return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2); + return new MipsInstructionSetFeatures(smp, fpu_32bit, mips_isa_gte2, r6); } } // namespace art diff --git a/runtime/arch/mips/instruction_set_features_mips.h b/runtime/arch/mips/instruction_set_features_mips.h index f7c64fef10..aac436e468 100644 --- a/runtime/arch/mips/instruction_set_features_mips.h +++ b/runtime/arch/mips/instruction_set_features_mips.h @@ -67,6 +67,10 @@ class MipsInstructionSetFeatures FINAL : public InstructionSetFeatures { return fpu_32bit_; } + bool IsR6() const { + return r6_; + } + virtual ~MipsInstructionSetFeatures() {} protected: @@ -76,19 +80,21 @@ class MipsInstructionSetFeatures FINAL : public InstructionSetFeatures { std::string* error_msg) const OVERRIDE; private: - MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2) - : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit), mips_isa_gte2_(mips_isa_gte2) { - } + MipsInstructionSetFeatures(bool smp, bool fpu_32bit, bool mips_isa_gte2, bool r6) + : InstructionSetFeatures(smp), fpu_32bit_(fpu_32bit), mips_isa_gte2_(mips_isa_gte2), r6_(r6) + {} // Bitmap positions for encoding features as a bitmap. enum { kSmpBitfield = 1, kFpu32Bitfield = 2, kIsaRevGte2Bitfield = 4, + kR6 = 8, }; const bool fpu_32bit_; const bool mips_isa_gte2_; + const bool r6_; DISALLOW_COPY_AND_ASSIGN(MipsInstructionSetFeatures); }; diff --git a/runtime/arch/mips/jni_entrypoints_mips.S b/runtime/arch/mips/jni_entrypoints_mips.S index 9a79467ede..fbc81d584b 100644 --- a/runtime/arch/mips/jni_entrypoints_mips.S +++ b/runtime/arch/mips/jni_entrypoints_mips.S @@ -47,9 +47,9 @@ ENTRY art_jni_dlsym_lookup_stub addiu $sp, $sp, 32 # restore the stack .cfi_adjust_cfa_offset -32 move $t9, $v0 # put method code result in $t9 - jr $t9 # leaf call to method's code + jalr $zero, $t9 # leaf call to method's code nop .Lno_native_code_found: - jr $ra + jalr $zero, $ra nop END art_jni_dlsym_lookup_stub diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 509f9910a2..666528ac0a 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -154,7 +154,7 @@ .macro RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME_AND_RETURN RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME - jr $ra + jalr $zero, $ra nop .endm @@ -274,7 +274,7 @@ .macro DELIVER_PENDING_EXCEPTION SETUP_SAVE_ALL_CALLEE_SAVE_FRAME # save callee saves for throw la $t9, artDeliverPendingExceptionFromCode - jr $t9 # artDeliverPendingExceptionFromCode(Thread*) + jalr $zero, $t9 # artDeliverPendingExceptionFromCode(Thread*) move $a0, rSELF # pass Thread::Current .endm @@ -283,7 +283,7 @@ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $t0, 1f # success if no exception is pending nop - jr $ra + jalr $zero, $ra nop 1: DELIVER_PENDING_EXCEPTION @@ -293,7 +293,7 @@ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME bnez $v0, 1f # success? nop - jr $ra # return on success + jalr $zero, $ra # return on success nop 1: DELIVER_PENDING_EXCEPTION @@ -303,7 +303,7 @@ RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME beqz $v0, 1f # success? nop - jr $ra # return on success + jalr $zero, $ra # return on success nop 1: DELIVER_PENDING_EXCEPTION @@ -365,7 +365,7 @@ ENTRY art_quick_do_long_jump lw $ra, 124($a0) lw $a0, 16($a0) move $v0, $zero # clear result registers r0 and r1 - jr $ra # do long jump + jalr $zero, $ra # do long jump move $v1, $zero END art_quick_do_long_jump @@ -377,7 +377,7 @@ END art_quick_do_long_jump ENTRY art_quick_deliver_exception SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artDeliverExceptionFromCode - jr $t9 # artDeliverExceptionFromCode(Throwable*, Thread*) + jalr $zero, $t9 # artDeliverExceptionFromCode(Throwable*, Thread*) move $a1, rSELF # pass Thread::Current END art_quick_deliver_exception @@ -388,7 +388,7 @@ END art_quick_deliver_exception ENTRY art_quick_throw_null_pointer_exception SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowNullPointerExceptionFromCode - jr $t9 # artThrowNullPointerExceptionFromCode(Thread*) + jalr $zero, $t9 # artThrowNullPointerExceptionFromCode(Thread*) move $a0, rSELF # pass Thread::Current END art_quick_throw_null_pointer_exception @@ -399,7 +399,7 @@ END art_quick_throw_null_pointer_exception ENTRY art_quick_throw_div_zero SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowDivZeroFromCode - jr $t9 # artThrowDivZeroFromCode(Thread*) + jalr $zero, $t9 # artThrowDivZeroFromCode(Thread*) move $a0, rSELF # pass Thread::Current END art_quick_throw_div_zero @@ -410,7 +410,7 @@ END art_quick_throw_div_zero ENTRY art_quick_throw_array_bounds SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowArrayBoundsFromCode - jr $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*) + jalr $zero, $t9 # artThrowArrayBoundsFromCode(index, limit, Thread*) move $a2, rSELF # pass Thread::Current END art_quick_throw_array_bounds @@ -421,7 +421,7 @@ END art_quick_throw_array_bounds ENTRY art_quick_throw_stack_overflow SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowStackOverflowFromCode - jr $t9 # artThrowStackOverflowFromCode(Thread*) + jalr $zero, $t9 # artThrowStackOverflowFromCode(Thread*) move $a0, rSELF # pass Thread::Current END art_quick_throw_stack_overflow @@ -432,7 +432,7 @@ END art_quick_throw_stack_overflow ENTRY art_quick_throw_no_such_method SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowNoSuchMethodFromCode - jr $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*) + jalr $zero, $t9 # artThrowNoSuchMethodFromCode(method_idx, Thread*) move $a1, rSELF # pass Thread::Current END art_quick_throw_no_such_method @@ -465,7 +465,7 @@ ENTRY \c_name RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME beqz $v0, 1f move $t9, $v1 # save $v0->code_ - jr $t9 + jalr $zero, $t9 nop 1: DELIVER_PENDING_EXCEPTION @@ -540,11 +540,11 @@ ENTRY art_quick_invoke_stub li $t3, 70 # put char 'F' into t3 beq $t1, $t3, 1f # branch if result type char == 'F' sw $v0, 0($t0) # store the result - jr $ra + jalr $zero, $ra sw $v1, 4($t0) # store the other half of the result 1: SDu $f0, $f1, 0, $t0, $t1 # store floating point result - jr $ra + jalr $zero, $ra nop END art_quick_invoke_stub @@ -604,7 +604,7 @@ ENTRY art_quick_check_cast addiu $sp, $sp, 16 beqz $v0, .Lthrow_class_cast_exception lw $ra, 12($sp) - jr $ra + jalr $zero, $ra addiu $sp, $sp, 16 .cfi_adjust_cfa_offset -16 .Lthrow_class_cast_exception: @@ -615,7 +615,7 @@ ENTRY art_quick_check_cast .cfi_adjust_cfa_offset -16 SETUP_SAVE_ALL_CALLEE_SAVE_FRAME la $t9, artThrowClassCastException - jr $t9 # artThrowClassCastException (Class*, Class*, Thread*) + jalr $zero, $t9 # artThrowClassCastException (Class*, Class*, Thread*) move $a2, rSELF # pass Thread::Current END art_quick_check_cast @@ -657,13 +657,13 @@ ENTRY art_quick_aput_obj srl $t1, $a0, 7 add $t1, $t1, $t0 sb $t0, ($t1) - jr $ra + jalr $zero, $ra nop .Ldo_aput_null: sll $a1, $a1, 2 add $t0, $a0, $a1 sw $a2, MIRROR_OBJECT_ARRAY_DATA_OFFSET($t0) - jr $ra + jalr $zero, $ra nop .Lcheck_assignability: addiu $sp, $sp, -32 @@ -691,7 +691,7 @@ ENTRY art_quick_aput_obj SETUP_SAVE_ALL_CALLEE_SAVE_FRAME move $a1, $a2 la $t9, artThrowArrayStoreException - jr $t9 # artThrowArrayStoreException(Class*, Class*, Thread*) + jalr $zero, $t9 # artThrowArrayStoreException(Class*, Class*, Thread*) move $a2, rSELF # pass Thread::Current END art_quick_aput_obj @@ -901,6 +901,7 @@ END art_quick_set32_static .extern artSet64StaticFromCode ENTRY art_quick_set64_static lw $a1, 0($sp) # pass referrer's Method* + # 64 bit new_val is in a2:a3 pair SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC jal artSet64StaticFromCode # (field_idx, referrer, new_val, Thread*) sw rSELF, 16($sp) # pass Thread::Current @@ -961,6 +962,7 @@ END art_quick_set32_instance .extern artSet64InstanceFromCode ENTRY art_quick_set64_instance lw $t1, 0($sp) # load referrer's Method* + # 64 bit new_val is in a2:a3 pair SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves in case of GC sw rSELF, 20($sp) # pass Thread::Current jal artSet64InstanceFromCode # (field_idx, Object*, new_val, referrer, Thread*) @@ -1038,7 +1040,7 @@ ENTRY art_quick_test_suspend lh $a0, THREAD_FLAGS_OFFSET(rSELF) bnez $a0, 1f addiu rSUSPEND, $zero, SUSPEND_CHECK_INTERVAL # reset rSUSPEND to SUSPEND_CHECK_INTERVAL - jr $ra + jalr $zero, $ra nop 1: SETUP_REFS_ONLY_CALLEE_SAVE_FRAME # save callee saves for stack crawl @@ -1062,7 +1064,7 @@ ENTRY art_quick_proxy_invoke_handler bnez $t0, 1f # don't care if $v0 and/or $v1 are modified, when exception branch taken MTD $v0, $v1, $f0, $f1 # move float value to return value - jr $ra + jalr $zero, $ra nop 1: DELIVER_PENDING_EXCEPTION @@ -1079,7 +1081,7 @@ ENTRY art_quick_imt_conflict_trampoline add $a0, $t0 # get address of target method lw $a0, MIRROR_OBJECT_ARRAY_DATA_OFFSET($a0) # load the target method la $t9, art_quick_invoke_interface_trampoline - jr $t9 + jalr $zero, $t9 END art_quick_imt_conflict_trampoline .extern artQuickResolutionTrampoline @@ -1092,7 +1094,7 @@ ENTRY art_quick_resolution_trampoline lw $a0, ARG_SLOT_SIZE($sp) # load resolved method to $a0 RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME move $t9, $v0 # code pointer must be in $t9 to generate the global pointer - jr $v0 # tail call to method + jalr $zero, $v0 # tail call to method nop 1: RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME @@ -1150,7 +1152,7 @@ ENTRY art_quick_generic_jni_trampoline RESTORE_REFS_AND_ARGS_CALLEE_SAVE_FRAME MTD $v0, $v1, $f0, $f1 # move float value to return value - jr $ra + jalr $zero, $ra nop 1: @@ -1171,7 +1173,7 @@ ENTRY art_quick_to_interpreter_bridge bnez $t0, 1f # don't care if $v0 and/or $v1 are modified, when exception branch taken MTD $v0, $v1, $f0, $f1 # move float value to return value - jr $ra + jalr $zero, $ra nop 1: DELIVER_PENDING_EXCEPTION @@ -1222,7 +1224,7 @@ art_quick_instrumentation_exit: lw $v0, 12($sp) # restore return values lw $v1, 8($sp) l.d $f0, 0($sp) - jr $t0 # return + jalr $zero, $t0 # return addiu $sp, $sp, 16 # remove temp storage from stack .cfi_adjust_cfa_offset -16 END art_quick_instrumentation_exit @@ -1263,7 +1265,7 @@ ENTRY_NO_GP art_quick_shl_long move $v1, $v0 # rhi<- rlo (if shift&0x20) move $v0, $zero # rlo<- 0 (if shift&0x20) -1: jr $ra +1: jalr $zero, $ra nop END art_quick_shl_long @@ -1291,7 +1293,7 @@ ENTRY_NO_GP art_quick_shr_long move $v0, $v1 # rlo<- rhi (if shift&0x20) move $v1, $a3 # rhi<- sign(ahi) (if shift&0x20) -1: jr $ra +1: jalr $zero, $ra nop END art_quick_shr_long @@ -1319,7 +1321,7 @@ ENTRY_NO_GP art_quick_ushr_long move $v0, $v1 # rlo<- rhi (if shift&0x20) move $v1, $zero # rhi<- 0 (if shift&0x20) -1: jr $ra +1: jalr $zero, $ra nop END art_quick_ushr_long diff --git a/runtime/debugger.cc b/runtime/debugger.cc index fe1e3a4aa5..229a1af6b1 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -819,10 +819,15 @@ void Dbg::Disconnected() { } gDebuggerActive = false; } - gRegistry->Clear(); - gDebuggerConnected = false; CHECK_EQ(self->SetStateUnsafe(old_state), kRunnable); runtime->GetThreadList()->ResumeAll(); + + { + ScopedObjectAccess soa(self); + gRegistry->Clear(); + } + + gDebuggerConnected = false; } bool Dbg::IsDebuggerActive() { diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 4198905e23..b6df6097bd 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -1332,7 +1332,10 @@ bool ElfFileImpl<Elf_Ehdr, Elf_Phdr, Elf_Shdr, Elf_Word, break; } case EM_MIPS: { - elf_ISA = kMips; + if ((GetHeader().e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R2 || + (GetHeader().e_flags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + elf_ISA = kMips; + } break; } } diff --git a/runtime/elf_utils.h b/runtime/elf_utils.h index 7b00bada47..3579e27953 100644 --- a/runtime/elf_utils.h +++ b/runtime/elf_utils.h @@ -30,6 +30,7 @@ namespace art { #define EF_ARM_EABI_VER5 0x05000000 #define EF_MIPS_ABI_O32 0x00001000 #define EF_MIPS_ARCH_32R2 0x70000000 +#define EF_MIPS_ARCH_32R6 0x90000000 #define EI_ABIVERSION 8 #define EM_ARM 40 diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 4bec70acd6..ac640b4454 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -59,6 +59,7 @@ class QuickArgumentVisitor { // | S0 | // | | 4x2 bytes padding // | Method* | <- sp + static constexpr bool kAlignPairRegister = !kArm32QuickCodeUseSoftFloat; static constexpr bool kQuickSoftFloatAbi = kArm32QuickCodeUseSoftFloat; static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = !kArm32QuickCodeUseSoftFloat; static constexpr size_t kNumQuickGprArgs = 3; @@ -93,6 +94,7 @@ class QuickArgumentVisitor { // | D0 | // | | padding // | Method* | <- sp + static constexpr bool kAlignPairRegister = false; static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI. static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false; static constexpr size_t kNumQuickGprArgs = 7; // 7 arguments passed in GPRs. @@ -121,6 +123,7 @@ class QuickArgumentVisitor { // | A2 | arg2 // | A1 | arg1 // | A0/Method* | <- sp + static constexpr bool kAlignPairRegister = false; static constexpr bool kQuickSoftFloatAbi = true; // This is a soft float ABI. static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false; static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs. @@ -146,6 +149,7 @@ class QuickArgumentVisitor { // | EDX | arg2 // | ECX | arg1 // | EAX/Method* | <- sp + static constexpr bool kAlignPairRegister = false; static constexpr bool kQuickSoftFloatAbi = true; // This is a soft float ABI. static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false; static constexpr size_t kNumQuickGprArgs = 3; // 3 arguments passed in GPRs. @@ -184,6 +188,7 @@ class QuickArgumentVisitor { // | XMM0 | float arg 1 // | Padding | // | RDI/Method* | <- sp + static constexpr bool kAlignPairRegister = false; static constexpr bool kQuickSoftFloatAbi = false; // This is a hard float ABI. static constexpr bool kQuickDoubleRegAlignedFloatBackFilled = false; static constexpr size_t kNumQuickGprArgs = 5; // 5 arguments passed in GPRs. @@ -370,6 +375,11 @@ class QuickArgumentVisitor { case Primitive::kPrimDouble: case Primitive::kPrimLong: if (kQuickSoftFloatAbi || (cur_type_ == Primitive::kPrimLong)) { + if (cur_type_ == Primitive::kPrimLong && kAlignPairRegister && gpr_index_ == 0) { + // Currently, this is only for ARM, where the first available parameter register + // is R1. So we skip it, and use R2 instead. + gpr_index_++; + } is_split_long_or_double_ = (GetBytesPerGprSpillLocation(kRuntimeISA) == 4) && ((gpr_index_ + 1) == kNumQuickGprArgs); Visit(); diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 681bfaac83..82d6992922 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -251,13 +251,19 @@ void SemiSpace::MarkingPhase() { // Note: Freed bytes can be negative if we copy form a compacted space to a free-list backed // space. RecordFree(ObjectBytePair(from_objects - to_objects, from_bytes - to_bytes)); - // Clear the from space. Protect it with PROT_READ here and if - // kProtectFromSpace is true, will protect it with PROT_NONE later - // in FinishPhase() so the rosalloc verification works (can read the - // metadata magic number.) + // Clear and protect the from space. from_space_->Clear(); - VLOG(heap) << "Protecting from_space_ with PROT_READ : " << *from_space_; - from_space_->GetMemMap()->Protect(PROT_READ); + if (kProtectFromSpace && !from_space_->IsRosAllocSpace()) { + // Protect with PROT_NONE. + VLOG(heap) << "Protecting from_space_ : " << *from_space_; + from_space_->GetMemMap()->Protect(PROT_NONE); + } else { + // If RosAllocSpace, we'll leave it as PROT_READ here so the + // rosaloc verification can read the metadata magic number and + // protect it with PROT_NONE later in FinishPhase(). + VLOG(heap) << "Protecting from_space_ with PROT_READ : " << *from_space_; + from_space_->GetMemMap()->Protect(PROT_READ); + } heap_->PreSweepingGcVerification(this); if (swap_semi_spaces_) { heap_->SwapSemiSpaces(); @@ -752,7 +758,7 @@ void SemiSpace::SetFromSpace(space::ContinuousMemMapAllocSpace* from_space) { void SemiSpace::FinishPhase() { TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - if (kProtectFromSpace) { + if (kProtectFromSpace && from_space_->IsRosAllocSpace()) { VLOG(heap) << "Protecting from_space_ with PROT_NONE : " << *from_space_; from_space_->GetMemMap()->Protect(PROT_NONE); } diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc index 42d2610e3e..1716d5e0e7 100644 --- a/runtime/hprof/hprof.cc +++ b/runtime/hprof/hprof.cc @@ -65,44 +65,15 @@ namespace hprof { static constexpr bool kDirectStream = true; -#define HPROF_TIME 0 -#define HPROF_NULL_STACK_TRACE 0 -#define HPROF_NULL_THREAD 0 - -#define U2_TO_BUF_BE(buf, offset, value) \ - do { \ - unsigned char* buf_ = (unsigned char*)(buf); \ - int offset_ = static_cast<int>(offset); \ - uint16_t value_ = (uint16_t)(value); \ - buf_[offset_ + 0] = (unsigned char)(value_ >> 8); \ - buf_[offset_ + 1] = (unsigned char)(value_ ); \ - } while (0) - -#define U4_TO_BUF_BE(buf, offset, value) \ - do { \ - unsigned char* buf_ = (unsigned char*)(buf); \ - int offset_ = static_cast<int>(offset); \ - uint32_t value_ = (uint32_t)(value); \ - buf_[offset_ + 0] = (unsigned char)(value_ >> 24); \ - buf_[offset_ + 1] = (unsigned char)(value_ >> 16); \ - buf_[offset_ + 2] = (unsigned char)(value_ >> 8); \ - buf_[offset_ + 3] = (unsigned char)(value_ ); \ - } while (0) - -#define U8_TO_BUF_BE(buf, offset, value) \ - do { \ - unsigned char* buf_ = (unsigned char*)(buf); \ - int offset_ = static_cast<int>(offset); \ - uint64_t value_ = (uint64_t)(value); \ - buf_[offset_ + 0] = (unsigned char)(value_ >> 56); \ - buf_[offset_ + 1] = (unsigned char)(value_ >> 48); \ - buf_[offset_ + 2] = (unsigned char)(value_ >> 40); \ - buf_[offset_ + 3] = (unsigned char)(value_ >> 32); \ - buf_[offset_ + 4] = (unsigned char)(value_ >> 24); \ - buf_[offset_ + 5] = (unsigned char)(value_ >> 16); \ - buf_[offset_ + 6] = (unsigned char)(value_ >> 8); \ - buf_[offset_ + 7] = (unsigned char)(value_ ); \ - } while (0) +static constexpr uint32_t kHprofTime = 0; +static constexpr uint32_t kHprofNullStackTrace = 0; +static constexpr uint32_t kHprofNullThread = 0; + +static constexpr size_t kMaxObjectsPerSegment = 128; +static constexpr size_t kMaxBytesPerSegment = 4096; + +// The static field-name for the synthetic object generated to account for class static overhead. +static constexpr const char* kStaticOverheadName = "$staticOverhead"; enum HprofTag { HPROF_TAG_STRING = 0x01, @@ -172,44 +143,43 @@ enum HprofBasicType { typedef uint32_t HprofStringId; typedef uint32_t HprofClassObjectId; -class Hprof; - -// Represents a top-level hprof record, whose serialized format is: -// U1 TAG: denoting the type of the record -// U4 TIME: number of microseconds since the time stamp in the header -// U4 LENGTH: number of bytes that follow this uint32_t field and belong to this record -// U1* BODY: as many bytes as specified in the above uint32_t field -class HprofRecord { +class EndianOutput { public: - explicit HprofRecord(Hprof* hprof) : alloc_length_(128), fp_(nullptr), tag_(0), time_(0), - length_(0), dirty_(false), hprof_(hprof) { - body_ = reinterpret_cast<unsigned char*>(malloc(alloc_length_)); - } + EndianOutput() : length_(0), sum_length_(0), max_length_(0), started_(false) {} + virtual ~EndianOutput() {} - ~HprofRecord() { - free(body_); + void StartNewRecord(uint8_t tag, uint32_t time) { + if (length_ > 0) { + EndRecord(); + } + DCHECK_EQ(length_, 0U); + AddU1(tag); + AddU4(time); + AddU4(0xdeaddead); // Length, replaced on flush. + started_ = true; } - // Returns how many characters were in the buffer (or written). - size_t StartNewRecord(FILE* fp, uint8_t tag, uint32_t time) WARN_UNUSED { - const size_t ret = Flush(); - fp_ = fp; - tag_ = tag; - time_ = time; - length_ = 0; - dirty_ = true; - return ret; - } + void EndRecord() { + // Replace length in header. + if (started_) { + UpdateU4(sizeof(uint8_t) + sizeof(uint32_t), + length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t)); + } - // Returns how many characters were in the buffer (or written). - size_t Flush() WARN_UNUSED; + HandleEndRecord(); - void AddU1(uint8_t value); + sum_length_ += length_; + max_length_ = std::max(max_length_, length_); + length_ = 0; + started_ = false; + } + void AddU1(uint8_t value) { + AddU1List(&value, 1); + } void AddU2(uint16_t value) { AddU2List(&value, 1); } - void AddU4(uint32_t value) { AddU4List(&value, 1); } @@ -239,14 +209,28 @@ class HprofRecord { AddU4(value); } - void AddU1List(const uint8_t* values, size_t numValues); - void AddU2List(const uint16_t* values, size_t numValues); - void AddU4List(const uint32_t* values, size_t numValues); - void UpdateU4(size_t offset, uint32_t new_value); - void AddU8List(const uint64_t* values, size_t numValues); + void AddU1List(const uint8_t* values, size_t count) { + HandleU1List(values, count); + length_ += count; + } + void AddU2List(const uint16_t* values, size_t count) { + HandleU2List(values, count); + length_ += count * sizeof(uint16_t); + } + void AddU4List(const uint32_t* values, size_t count) { + HandleU4List(values, count); + length_ += count * sizeof(uint32_t); + } + virtual void UpdateU4(size_t offset ATTRIBUTE_UNUSED, uint32_t new_value ATTRIBUTE_UNUSED) { + DCHECK_LE(offset, length_ - 4); + } + void AddU8List(const uint64_t* values, size_t count) { + HandleU8List(values, count); + length_ += count * sizeof(uint64_t); + } void AddIdList(mirror::ObjectArray<mirror::Object>* values) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { const int32_t length = values->GetLength(); for (int32_t i = 0; i < length; ++i) { AddObjectId(values->GetWithoutChecks(i)); @@ -258,301 +242,342 @@ class HprofRecord { AddU1List((const uint8_t*)str, strlen(str)); } - size_t Size() const { + size_t Length() const { return length_; } - private: - void GuaranteeRecordAppend(size_t nmore) { - const size_t min_size = length_ + nmore; - if (min_size > alloc_length_) { - const size_t new_alloc_len = std::max(alloc_length_ * 2, min_size); - body_ = (unsigned char*)realloc(body_, new_alloc_len); - CHECK(body_ != nullptr); - alloc_length_ = new_alloc_len; - } - CHECK_LE(length_ + nmore, alloc_length_); + size_t SumLength() const { + return sum_length_; } - size_t alloc_length_; - unsigned char* body_; + size_t MaxLength() const { + return max_length_; + } - FILE* fp_; - uint8_t tag_; - uint32_t time_; - size_t length_; - bool dirty_; - Hprof* hprof_; + protected: + virtual void HandleU1List(const uint8_t* values ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) { + } + virtual void HandleU2List(const uint16_t* values ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) { + } + virtual void HandleU4List(const uint32_t* values ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) { + } + virtual void HandleU8List(const uint64_t* values ATTRIBUTE_UNUSED, + size_t count ATTRIBUTE_UNUSED) { + } + virtual void HandleEndRecord() { + } - DISALLOW_COPY_AND_ASSIGN(HprofRecord); + size_t length_; // Current record size. + size_t sum_length_; // Size of all data. + size_t max_length_; // Maximum seen length. + bool started_; // Was StartRecord called? }; -class Hprof { +// This keeps things buffered until flushed. +class EndianOutputBuffered : public EndianOutput { public: - Hprof(const char* output_filename, int fd, bool direct_to_ddms) - : filename_(output_filename), - fd_(fd), - direct_to_ddms_(direct_to_ddms), - start_ns_(NanoTime()), - current_record_(this), - gc_thread_serial_number_(0), - gc_scan_state_(0), - current_heap_(HPROF_HEAP_DEFAULT), - objects_in_segment_(0), - header_fp_(nullptr), - header_data_ptr_(nullptr), - header_data_size_(0), - body_fp_(nullptr), - body_data_ptr_(nullptr), - body_data_size_(0), - net_state_(nullptr), - next_string_id_(0x400000) { - LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting..."; + explicit EndianOutputBuffered(size_t reserve_size) { + buffer_.reserve(reserve_size); + } + virtual ~EndianOutputBuffered() {} + + void UpdateU4(size_t offset, uint32_t new_value) OVERRIDE { + DCHECK_LE(offset, length_ - 4); + buffer_[offset + 0] = static_cast<uint8_t>((new_value >> 24) & 0xFF); + buffer_[offset + 1] = static_cast<uint8_t>((new_value >> 16) & 0xFF); + buffer_[offset + 2] = static_cast<uint8_t>((new_value >> 8) & 0xFF); + buffer_[offset + 3] = static_cast<uint8_t>((new_value >> 0) & 0xFF); } - ~Hprof() { - if (header_fp_ != nullptr) { - fclose(header_fp_); + protected: + void HandleU1List(const uint8_t* values, size_t count) OVERRIDE { + DCHECK_EQ(length_, buffer_.size()); + buffer_.insert(buffer_.end(), values, values + count); + } + + void HandleU2List(const uint16_t* values, size_t count) OVERRIDE { + DCHECK_EQ(length_, buffer_.size()); + for (size_t i = 0; i < count; ++i) { + uint16_t value = *values; + buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF)); + values++; } - if (body_fp_ != nullptr) { - fclose(body_fp_); + } + + void HandleU4List(const uint32_t* values, size_t count) OVERRIDE { + DCHECK_EQ(length_, buffer_.size()); + for (size_t i = 0; i < count; ++i) { + uint32_t value = *values; + buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF)); + values++; } - free(header_data_ptr_); - free(body_data_ptr_); } - void ProcessBody() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - Runtime* runtime = Runtime::Current(); - // Walk the roots and the heap. - total_body_bytes_ += current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, - HPROF_TIME); - runtime->VisitRoots(RootVisitor, this); - runtime->GetHeap()->VisitObjects(VisitObjectCallback, this); - total_body_bytes_ += current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_END, - HPROF_TIME); - total_body_bytes_ += current_record_.Flush(); - if (allow_writing_) { - fflush(body_fp_); + void HandleU8List(const uint64_t* values, size_t count) OVERRIDE { + DCHECK_EQ(length_, buffer_.size()); + for (size_t i = 0; i < count; ++i) { + uint64_t value = *values; + buffer_.push_back(static_cast<uint8_t>((value >> 56) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 48) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 40) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 32) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 24) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 16) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 8) & 0xFF)); + buffer_.push_back(static_cast<uint8_t>((value >> 0) & 0xFF)); + values++; } } - void ProcessHeader() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Write the header. - WriteFixedHeader(); - // Write the string and class tables, and any stack traces, to the header. - // (jhat requires that these appear before any of the data in the body that refers to them.) - WriteStringTable(); - WriteClassTable(); - WriteStackTraces(); - total_header_bytes_ += current_record_.Flush(); - if (allow_writing_) { - fflush(header_fp_); + void HandleEndRecord() OVERRIDE { + DCHECK_EQ(buffer_.size(), length_); + if (kIsDebugBuild && started_) { + uint32_t stored_length = + static_cast<uint32_t>(buffer_[5]) << 24 | + static_cast<uint32_t>(buffer_[6]) << 16 | + static_cast<uint32_t>(buffer_[7]) << 8 | + static_cast<uint32_t>(buffer_[8]); + DCHECK_EQ(stored_length, length_ - sizeof(uint8_t) - 2 * sizeof(uint32_t)); } + HandleFlush(buffer_.data(), length_); + buffer_.clear(); } - void ProcessHeapStreaming(size_t data_len, uint32_t chunk_type) - EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - total_body_bytes_ = 0; - total_header_bytes_ = 0; - allow_writing_ = true; - CHECK(direct_to_ddms_); - JDWP::JdwpState* state = Dbg::GetJdwpState(); - CHECK(state != nullptr); - net_state_ = state->netState; - CHECK(net_state_ != nullptr); - // Hold the socket lock for the whole tiem since we want this to be atomic. - MutexLock mu(Thread::Current(), *net_state_->GetSocketLock()); - total_body_bytes_ = 0; - total_header_bytes_ = 0; - constexpr size_t kChunkHeaderSize = kJDWPHeaderLen + 8; - uint8_t chunk_header[kChunkHeaderSize] = { 0 }; - state->SetupChunkHeader(chunk_type, data_len, kChunkHeaderSize, chunk_header); - Write(chunk_header, kChunkHeaderSize, nullptr); // Send the header chunk to DDMS. - ProcessHeader(); - ProcessBody(); - CHECK_EQ(total_body_bytes_ + total_header_bytes_, data_len); - net_state_ = nullptr; - } - void ProcessHeap(bool allow_writing) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) - SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { - allow_writing_ = allow_writing; - total_body_bytes_ = 0; - total_header_bytes_ = 0; - if (allow_writing) { - header_fp_ = open_memstream(&header_data_ptr_, &header_data_size_); - CHECK(header_fp_ != nullptr) << "header open_memstream failed"; - body_fp_ = open_memstream(&body_data_ptr_, &body_data_size_); - CHECK(body_fp_ != nullptr) << "body open_memstream failed"; + virtual void HandleFlush(const uint8_t* buffer ATTRIBUTE_UNUSED, size_t length ATTRIBUTE_UNUSED) { + } + + std::vector<uint8_t> buffer_; +}; + +class FileEndianOutput FINAL : public EndianOutputBuffered { + public: + FileEndianOutput(File* fp, size_t reserved_size) + : EndianOutputBuffered(reserved_size), fp_(fp), errors_(false) { + DCHECK(fp != nullptr); + } + ~FileEndianOutput() { + } + + bool Errors() { + return errors_; + } + + protected: + void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE { + if (!errors_) { + errors_ = !fp_->WriteFully(buffer, length); } - ProcessBody(); - ProcessHeader(); } - void Dump() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + private: + File* fp_; + bool errors_; +}; + +class NetStateEndianOutput FINAL : public EndianOutputBuffered { + public: + NetStateEndianOutput(JDWP::JdwpNetStateBase* net_state, size_t reserved_size) + : EndianOutputBuffered(reserved_size), net_state_(net_state) { + DCHECK(net_state != nullptr); + } + ~NetStateEndianOutput() {} + + protected: + void HandleFlush(const uint8_t* buffer, size_t length) OVERRIDE { + std::vector<iovec> iov; + iov.push_back(iovec()); + iov[0].iov_base = const_cast<void*>(reinterpret_cast<const void*>(buffer)); + iov[0].iov_len = length; + net_state_->WriteBufferedPacketLocked(iov); + } + + private: + JDWP::JdwpNetStateBase* net_state_; +}; + +#define __ output-> + +class Hprof { + public: + Hprof(const char* output_filename, int fd, bool direct_to_ddms) + : filename_(output_filename), + fd_(fd), + direct_to_ddms_(direct_to_ddms), + start_ns_(NanoTime()), + current_heap_(HPROF_HEAP_DEFAULT), + objects_in_segment_(0), + next_string_id_(0x400000) { + LOG(INFO) << "hprof: heap dump \"" << filename_ << "\" starting..."; + } + + void Dump() + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::heap_bitmap_lock_) { + ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); + // First pass to measure the size of the dump. + size_t overall_size; + size_t max_length; { - ReaderMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); - // First pass to measure the size of the dump. - ProcessHeap(false); - const size_t header_bytes = total_header_bytes_; - const size_t body_bytes = total_body_bytes_; - if (direct_to_ddms_ && kDirectStream) { - ProcessHeapStreaming(header_bytes + body_bytes, CHUNK_TYPE("HPDS")); - } else { - ProcessHeap(true); - CHECK_EQ(header_data_size_, header_bytes); - CHECK_EQ(body_data_size_, body_bytes); - } - CHECK_EQ(total_header_bytes_, header_bytes); - CHECK_EQ(total_body_bytes_, body_bytes); + EndianOutput count_output; + ProcessHeap(&count_output, false); + overall_size = count_output.SumLength(); + max_length = count_output.MaxLength(); } - bool okay = true; - if (!kDirectStream) { - if (direct_to_ddms_) { - // Send the data off to DDMS. - iovec iov[2]; - iov[0].iov_base = header_data_ptr_; - iov[0].iov_len = header_data_size_; - iov[1].iov_base = body_data_ptr_; - iov[1].iov_len = body_data_size_; - Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2); + bool okay; + if (direct_to_ddms_) { + if (kDirectStream) { + okay = DumpToDdmsDirect(overall_size, max_length, CHUNK_TYPE("HPDS")); } else { - // Where exactly are we writing to? - int out_fd; - if (fd_ >= 0) { - out_fd = dup(fd_); - if (out_fd < 0) { - ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno)); - return; - } - } else { - out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644); - if (out_fd < 0) { - ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(), - strerror(errno)); - return; - } - } - - std::unique_ptr<File> file(new File(out_fd, filename_, true)); - okay = file->WriteFully(header_data_ptr_, header_data_size_) && - file->WriteFully(body_data_ptr_, body_data_size_); - if (okay) { - okay = file->FlushCloseOrErase() == 0; - } else { - file->Erase(); - } - if (!okay) { - std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", - filename_.c_str(), strerror(errno))); - ThrowRuntimeException("%s", msg.c_str()); - LOG(ERROR) << msg; - } + okay = DumpToDdmsBuffered(overall_size, max_length); } + } else { + okay = DumpToFile(overall_size, max_length); } - // Throw out a log message for the benefit of "runhat". if (okay) { uint64_t duration = NanoTime() - start_ns_; LOG(INFO) << "hprof: heap dump completed (" - << PrettySize(total_header_bytes_ + total_body_bytes_ + 1023) + << PrettySize(RoundUp(overall_size, 1024)) << ") in " << PrettyDuration(duration); } } - bool AllowWriting() const { - return allow_writing_; - } - - size_t Write(const void* ptr, size_t len, FILE* fp) { - if (allow_writing_) { - if (net_state_ != nullptr) { - CHECK(fp == nullptr); - std::vector<iovec> iov; - iov.push_back(iovec()); - iov[0].iov_base = const_cast<void*>(ptr); - iov[0].iov_len = len; - net_state_->WriteBufferedPacketLocked(iov); - } else { - const size_t n = fwrite(ptr, 1, len, fp); - CHECK_EQ(n, len); - } - } - return len; - } - private: + struct Env { + Hprof* hprof; + EndianOutput* output; + }; + static void RootVisitor(mirror::Object** obj, void* arg, uint32_t thread_id, RootType root_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(arg != nullptr); DCHECK(obj != nullptr); DCHECK(*obj != nullptr); - reinterpret_cast<Hprof*>(arg)->VisitRoot(*obj, thread_id, root_type); + Env* env = reinterpret_cast<Env*>(arg); + env->hprof->VisitRoot(*obj, thread_id, root_type, env->output); } static void VisitObjectCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(obj != nullptr); DCHECK(arg != nullptr); - reinterpret_cast<Hprof*>(arg)->DumpHeapObject(obj); + Env* env = reinterpret_cast<Env*>(arg); + env->hprof->DumpHeapObject(obj, env->output); } - void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type) + void DumpHeapObject(mirror::Object* obj, EndianOutput* output) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void DumpHeapClass(mirror::Class* klass, EndianOutput* output) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - int DumpHeapObject(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass, EndianOutput* output) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void ProcessHeap(EndianOutput* output, bool header_first) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + // Reset current heap and object count. + current_heap_ = HPROF_HEAP_DEFAULT; + objects_in_segment_ = 0; + + if (header_first) { + ProcessHeader(output); + ProcessBody(output); + } else { + ProcessBody(output); + ProcessHeader(output); + } + } - void WriteClassTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - HprofRecord* rec = ¤t_record_; + void ProcessBody(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + Runtime* runtime = Runtime::Current(); + // Walk the roots and the heap. + output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime); + + Env env = { this, output }; + runtime->VisitRoots(RootVisitor, &env); + runtime->GetHeap()->VisitObjects(VisitObjectCallback, &env); + + output->StartNewRecord(HPROF_TAG_HEAP_DUMP_END, kHprofTime); + output->EndRecord(); + } + + void ProcessHeader(EndianOutput* output) EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Write the header. + WriteFixedHeader(output); + // Write the string and class tables, and any stack traces, to the header. + // (jhat requires that these appear before any of the data in the body that refers to them.) + WriteStringTable(output); + WriteClassTable(output); + WriteStackTraces(output); + output->EndRecord(); + } + + void WriteClassTable(EndianOutput* output) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { uint32_t nextSerialNumber = 1; for (mirror::Class* c : classes_) { CHECK(c != nullptr); - total_header_bytes_ += current_record_.StartNewRecord(header_fp_, HPROF_TAG_LOAD_CLASS, - HPROF_TIME); + output->StartNewRecord(HPROF_TAG_LOAD_CLASS, kHprofTime); // LOAD CLASS format: // U4: class serial number (always > 0) // ID: class object ID. We use the address of the class object structure as its ID. // U4: stack trace serial number // ID: class name string ID - rec->AddU4(nextSerialNumber++); - rec->AddObjectId(c); - rec->AddU4(HPROF_NULL_STACK_TRACE); - rec->AddStringId(LookupClassNameId(c)); + __ AddU4(nextSerialNumber++); + __ AddObjectId(c); + __ AddU4(kHprofNullStackTrace); + __ AddStringId(LookupClassNameId(c)); } } - void WriteStringTable() { - HprofRecord* rec = ¤t_record_; + void WriteStringTable(EndianOutput* output) { for (const std::pair<std::string, HprofStringId>& p : strings_) { const std::string& string = p.first; const size_t id = p.second; - total_header_bytes_ += current_record_.StartNewRecord(header_fp_, HPROF_TAG_STRING, - HPROF_TIME); + output->StartNewRecord(HPROF_TAG_STRING, kHprofTime); // STRING format: // ID: ID for this string // U1*: UTF8 characters for string (NOT NULL terminated) // (the record format encodes the length) - rec->AddU4(id); - rec->AddUtf8String(string.c_str()); + __ AddU4(id); + __ AddUtf8String(string.c_str()); } } - void StartNewHeapDumpSegment() { + void StartNewHeapDumpSegment(EndianOutput* output) { // This flushes the old segment and starts a new one. - total_body_bytes_ += current_record_.StartNewRecord(body_fp_, HPROF_TAG_HEAP_DUMP_SEGMENT, - HPROF_TIME); + output->StartNewRecord(HPROF_TAG_HEAP_DUMP_SEGMENT, kHprofTime); objects_in_segment_ = 0; // Starting a new HEAP_DUMP resets the heap to default. current_heap_ = HPROF_HEAP_DEFAULT; } - int MarkRootObject(const mirror::Object* obj, jobject jniObj); + void CheckHeapSegmentConstraints(EndianOutput* output) { + if (objects_in_segment_ >= kMaxObjectsPerSegment || output->Length() >= kMaxBytesPerSegment) { + StartNewHeapDumpSegment(output); + } + } + + void VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type, EndianOutput* output) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag, + uint32_t thread_serial, EndianOutput* output); HprofClassObjectId LookupClassId(mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (c != nullptr) { @@ -587,38 +612,128 @@ class Hprof { return LookupStringId(PrettyDescriptor(c)); } - void WriteFixedHeader() { - char magic[] = "JAVA PROFILE 1.0.3"; - unsigned char buf[4] = { 0 }; + void WriteFixedHeader(EndianOutput* output) { // Write the file header. // U1: NUL-terminated magic string. - total_header_bytes_ += Write(magic, sizeof(magic), header_fp_); + const char magic[] = "JAVA PROFILE 1.0.3"; + __ AddU1List(reinterpret_cast<const uint8_t*>(magic), sizeof(magic)); + // U4: size of identifiers. We're using addresses as IDs and our heap references are stored // as uint32_t. // Note of warning: hprof-conv hard-codes the size of identifiers to 4. static_assert(sizeof(mirror::HeapReference<mirror::Object>) == sizeof(uint32_t), "Unexpected HeapReference size"); - U4_TO_BUF_BE(buf, 0, sizeof(uint32_t)); - total_header_bytes_ += Write(buf, sizeof(uint32_t), header_fp_); + __ AddU4(sizeof(uint32_t)); + // The current time, in milliseconds since 0:00 GMT, 1/1/70. timeval now; - const uint64_t nowMs = (gettimeofday(&now, NULL) < 0) ? 0 : + const uint64_t nowMs = (gettimeofday(&now, nullptr) < 0) ? 0 : (uint64_t)now.tv_sec * 1000 + now.tv_usec / 1000; + // TODO: It seems it would be correct to use U8. // U4: high word of the 64-bit time. - U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs >> 32)); - total_header_bytes_ += Write(buf, sizeof(uint32_t), header_fp_); + __ AddU4(static_cast<uint32_t>(nowMs >> 32)); // U4: low word of the 64-bit time. - U4_TO_BUF_BE(buf, 0, (uint32_t)(nowMs & 0xffffffffULL)); - total_header_bytes_ += Write(buf, sizeof(uint32_t), header_fp_); // xxx fix the time + __ AddU4(static_cast<uint32_t>(nowMs & 0xFFFFFFFF)); } - void WriteStackTraces() { + void WriteStackTraces(EndianOutput* output) { // Write a dummy stack trace record so the analysis tools don't freak out. - total_header_bytes_ += - current_record_.StartNewRecord(header_fp_, HPROF_TAG_STACK_TRACE, HPROF_TIME); - current_record_.AddU4(HPROF_NULL_STACK_TRACE); - current_record_.AddU4(HPROF_NULL_THREAD); - current_record_.AddU4(0); // no frames + output->StartNewRecord(HPROF_TAG_STACK_TRACE, kHprofTime); + __ AddU4(kHprofNullStackTrace); + __ AddU4(kHprofNullThread); + __ AddU4(0); // no frames + } + + bool DumpToDdmsBuffered(size_t overall_size ATTRIBUTE_UNUSED, size_t max_length ATTRIBUTE_UNUSED) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + LOG(FATAL) << "Unimplemented"; + UNREACHABLE(); + // // Send the data off to DDMS. + // iovec iov[2]; + // iov[0].iov_base = header_data_ptr_; + // iov[0].iov_len = header_data_size_; + // iov[1].iov_base = body_data_ptr_; + // iov[1].iov_len = body_data_size_; + // Dbg::DdmSendChunkV(CHUNK_TYPE("HPDS"), iov, 2); + } + + bool DumpToFile(size_t overall_size, size_t max_length) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + // Where exactly are we writing to? + int out_fd; + if (fd_ >= 0) { + out_fd = dup(fd_); + if (out_fd < 0) { + ThrowRuntimeException("Couldn't dump heap; dup(%d) failed: %s", fd_, strerror(errno)); + return false; + } + } else { + out_fd = open(filename_.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (out_fd < 0) { + ThrowRuntimeException("Couldn't dump heap; open(\"%s\") failed: %s", filename_.c_str(), + strerror(errno)); + return false; + } + } + + std::unique_ptr<File> file(new File(out_fd, filename_, true)); + bool okay; + { + FileEndianOutput file_output(file.get(), max_length); + ProcessHeap(&file_output, true); + okay = !file_output.Errors(); + + if (okay) { + // Check for expected size. + CHECK_EQ(file_output.SumLength(), overall_size); + } + } + + if (okay) { + okay = file->FlushCloseOrErase() == 0; + } else { + file->Erase(); + } + if (!okay) { + std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s", + filename_.c_str(), strerror(errno))); + ThrowRuntimeException("%s", msg.c_str()); + LOG(ERROR) << msg; + } + + return okay; + } + + bool DumpToDdmsDirect(size_t overall_size, size_t max_length, uint32_t chunk_type) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) + SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_) { + CHECK(direct_to_ddms_); + JDWP::JdwpState* state = Dbg::GetJdwpState(); + CHECK(state != nullptr); + JDWP::JdwpNetStateBase* net_state = state->netState; + CHECK(net_state != nullptr); + + // Hold the socket lock for the whole time since we want this to be atomic. + MutexLock mu(Thread::Current(), *net_state->GetSocketLock()); + + // Prepare the Ddms chunk. + constexpr size_t kChunkHeaderSize = kJDWPHeaderLen + 8; + uint8_t chunk_header[kChunkHeaderSize] = { 0 }; + state->SetupChunkHeader(chunk_type, overall_size, kChunkHeaderSize, chunk_header); + + // Prepare the output and send the chunk header. + NetStateEndianOutput net_output(net_state, max_length); + net_output.AddU1List(chunk_header, kChunkHeaderSize); + + // Write the dump. + ProcessHeap(&net_output, true); + + // Check for expected size. + CHECK_EQ(net_output.SumLength(), overall_size + kChunkHeaderSize); + + return true; } // If direct_to_ddms_ is set, "filename_" and "fd" will be ignored. @@ -628,30 +743,11 @@ class Hprof { int fd_; bool direct_to_ddms_; - // Whether or not we are in the size calculating mode or writing mode. - bool allow_writing_; - uint64_t start_ns_; - HprofRecord current_record_; - - uint32_t gc_thread_serial_number_; - uint8_t gc_scan_state_; HprofHeapId current_heap_; // Which heap we're currently dumping. size_t objects_in_segment_; - FILE* header_fp_; - char* header_data_ptr_; - size_t header_data_size_; - size_t total_header_bytes_; - - FILE* body_fp_; - char* body_data_ptr_; - size_t body_data_size_; - size_t total_body_bytes_; - - JDWP::JdwpNetStateBase* net_state_; - std::set<mirror::Class*> classes_; HprofStringId next_string_id_; SafeMap<std::string, HprofStringId> strings_; @@ -659,56 +755,56 @@ class Hprof { DISALLOW_COPY_AND_ASSIGN(Hprof); }; -#define OBJECTS_PER_SEGMENT ((size_t)128) -#define BYTES_PER_SEGMENT ((size_t)4096) - -// The static field-name for the synthetic object generated to account for class static overhead. -#define STATIC_OVERHEAD_NAME "$staticOverhead" - -static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* sizeOut) { +static HprofBasicType SignatureToBasicTypeAndSize(const char* sig, size_t* size_out) { char c = sig[0]; HprofBasicType ret; size_t size; switch (c) { - case '[': - case 'L': ret = hprof_basic_object; size = 4; break; - case 'Z': ret = hprof_basic_boolean; size = 1; break; - case 'C': ret = hprof_basic_char; size = 2; break; - case 'F': ret = hprof_basic_float; size = 4; break; - case 'D': ret = hprof_basic_double; size = 8; break; - case 'B': ret = hprof_basic_byte; size = 1; break; - case 'S': ret = hprof_basic_short; size = 2; break; - case 'I': ret = hprof_basic_int; size = 4; break; - case 'J': ret = hprof_basic_long; size = 8; break; - default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE(); - } - - if (sizeOut != NULL) { - *sizeOut = size; - } - - return ret; -} - -static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t* sizeOut) { - HprofBasicType ret; - size_t size; - - switch (prim) { - case Primitive::kPrimBoolean: ret = hprof_basic_boolean; size = 1; break; - case Primitive::kPrimChar: ret = hprof_basic_char; size = 2; break; - case Primitive::kPrimFloat: ret = hprof_basic_float; size = 4; break; - case Primitive::kPrimDouble: ret = hprof_basic_double; size = 8; break; - case Primitive::kPrimByte: ret = hprof_basic_byte; size = 1; break; - case Primitive::kPrimShort: ret = hprof_basic_short; size = 2; break; - case Primitive::kPrimInt: ret = hprof_basic_int; size = 4; break; - case Primitive::kPrimLong: ret = hprof_basic_long; size = 8; break; - default: LOG(FATAL) << "UNREACHABLE"; UNREACHABLE(); + case '[': + case 'L': + ret = hprof_basic_object; + size = 4; + break; + case 'Z': + ret = hprof_basic_boolean; + size = 1; + break; + case 'C': + ret = hprof_basic_char; + size = 2; + break; + case 'F': + ret = hprof_basic_float; + size = 4; + break; + case 'D': + ret = hprof_basic_double; + size = 8; + break; + case 'B': + ret = hprof_basic_byte; + size = 1; + break; + case 'S': + ret = hprof_basic_short; + size = 2; + break; + case 'I': + ret = hprof_basic_int; + size = 4; + break; + case 'J': + ret = hprof_basic_long; + size = 8; + break; + default: + LOG(FATAL) << "UNREACHABLE"; + UNREACHABLE(); } - if (sizeOut != NULL) { - *sizeOut = size; + if (size_out != nullptr) { + *size_out = size; } return ret; @@ -718,95 +814,94 @@ static HprofBasicType PrimitiveToBasicTypeAndSize(Primitive::Type prim, size_t* // something when ctx->gc_scan_state_ is non-zero, which is usually // only true when marking the root set or unreachable // objects. Used to add rootset references to obj. -int Hprof::MarkRootObject(const mirror::Object* obj, jobject jniObj) { - HprofRecord* rec = ¤t_record_; - HprofHeapTag heapTag = (HprofHeapTag)gc_scan_state_; - - if (heapTag == 0) { - return 0; - } - - if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) { - StartNewHeapDumpSegment(); - } - - switch (heapTag) { - // ID: object ID - case HPROF_ROOT_UNKNOWN: - case HPROF_ROOT_STICKY_CLASS: - case HPROF_ROOT_MONITOR_USED: - case HPROF_ROOT_INTERNED_STRING: - case HPROF_ROOT_DEBUGGER: - case HPROF_ROOT_VM_INTERNAL: - rec->AddU1(heapTag); - rec->AddObjectId(obj); - break; - - // ID: object ID - // ID: JNI global ref ID - case HPROF_ROOT_JNI_GLOBAL: - rec->AddU1(heapTag); - rec->AddObjectId(obj); - rec->AddJniGlobalRefId(jniObj); - break; - - // ID: object ID - // U4: thread serial number - // U4: frame number in stack trace (-1 for empty) - case HPROF_ROOT_JNI_LOCAL: - case HPROF_ROOT_JNI_MONITOR: - case HPROF_ROOT_JAVA_FRAME: - rec->AddU1(heapTag); - rec->AddObjectId(obj); - rec->AddU4(gc_thread_serial_number_); - rec->AddU4((uint32_t)-1); - break; - - // ID: object ID - // U4: thread serial number - case HPROF_ROOT_NATIVE_STACK: - case HPROF_ROOT_THREAD_BLOCK: - rec->AddU1(heapTag); - rec->AddObjectId(obj); - rec->AddU4(gc_thread_serial_number_); - break; - - // ID: thread object ID - // U4: thread serial number - // U4: stack trace serial number - case HPROF_ROOT_THREAD_OBJECT: - rec->AddU1(heapTag); - rec->AddObjectId(obj); - rec->AddU4(gc_thread_serial_number_); - rec->AddU4((uint32_t)-1); // xxx - break; - - case HPROF_CLASS_DUMP: - case HPROF_INSTANCE_DUMP: - case HPROF_OBJECT_ARRAY_DUMP: - case HPROF_PRIMITIVE_ARRAY_DUMP: - case HPROF_HEAP_DUMP_INFO: - case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP: - // Ignored. - break; - - case HPROF_ROOT_FINALIZING: - case HPROF_ROOT_REFERENCE_CLEANUP: - case HPROF_UNREACHABLE: - LOG(FATAL) << "obsolete tag " << static_cast<int>(heapTag); - break; +void Hprof::MarkRootObject(const mirror::Object* obj, jobject jni_obj, HprofHeapTag heap_tag, + uint32_t thread_serial, EndianOutput* output) { + if (heap_tag == 0) { + return; + } + + CheckHeapSegmentConstraints(output); + + switch (heap_tag) { + // ID: object ID + case HPROF_ROOT_UNKNOWN: + case HPROF_ROOT_STICKY_CLASS: + case HPROF_ROOT_MONITOR_USED: + case HPROF_ROOT_INTERNED_STRING: + case HPROF_ROOT_DEBUGGER: + case HPROF_ROOT_VM_INTERNAL: + __ AddU1(heap_tag); + __ AddObjectId(obj); + break; + + // ID: object ID + // ID: JNI global ref ID + case HPROF_ROOT_JNI_GLOBAL: + __ AddU1(heap_tag); + __ AddObjectId(obj); + __ AddJniGlobalRefId(jni_obj); + break; + + // ID: object ID + // U4: thread serial number + // U4: frame number in stack trace (-1 for empty) + case HPROF_ROOT_JNI_LOCAL: + case HPROF_ROOT_JNI_MONITOR: + case HPROF_ROOT_JAVA_FRAME: + __ AddU1(heap_tag); + __ AddObjectId(obj); + __ AddU4(thread_serial); + __ AddU4((uint32_t)-1); + break; + + // ID: object ID + // U4: thread serial number + case HPROF_ROOT_NATIVE_STACK: + case HPROF_ROOT_THREAD_BLOCK: + __ AddU1(heap_tag); + __ AddObjectId(obj); + __ AddU4(thread_serial); + break; + + // ID: thread object ID + // U4: thread serial number + // U4: stack trace serial number + case HPROF_ROOT_THREAD_OBJECT: + __ AddU1(heap_tag); + __ AddObjectId(obj); + __ AddU4(thread_serial); + __ AddU4((uint32_t)-1); // xxx + break; + + case HPROF_CLASS_DUMP: + case HPROF_INSTANCE_DUMP: + case HPROF_OBJECT_ARRAY_DUMP: + case HPROF_PRIMITIVE_ARRAY_DUMP: + case HPROF_HEAP_DUMP_INFO: + case HPROF_PRIMITIVE_ARRAY_NODATA_DUMP: + // Ignored. + break; + + case HPROF_ROOT_FINALIZING: + case HPROF_ROOT_REFERENCE_CLEANUP: + case HPROF_UNREACHABLE: + LOG(FATAL) << "obsolete tag " << static_cast<int>(heap_tag); + break; } ++objects_in_segment_; - return 0; } static int StackTraceSerialNumber(const mirror::Object* /*obj*/) { - return HPROF_NULL_STACK_TRACE; + return kHprofNullStackTrace; } -int Hprof::DumpHeapObject(mirror::Object* obj) { - HprofRecord* rec = ¤t_record_; +void Hprof::DumpHeapObject(mirror::Object* obj, EndianOutput* output) { + // Ignore classes that are retired. + if (obj->IsClass() && obj->AsClass()->IsRetired()) { + return; + } + gc::space::ContinuousSpace* space = Runtime::Current()->GetHeap()->FindContinuousSpaceFromObject(obj, true); HprofHeapId heap_type = HPROF_HEAP_APP; @@ -817,17 +912,15 @@ int Hprof::DumpHeapObject(mirror::Object* obj) { heap_type = HPROF_HEAP_IMAGE; } } - if (objects_in_segment_ >= OBJECTS_PER_SEGMENT || rec->Size() >= BYTES_PER_SEGMENT) { - StartNewHeapDumpSegment(); - } + CheckHeapSegmentConstraints(output); if (heap_type != current_heap_) { HprofStringId nameId; // This object is in a different heap than the current one. // Emit a HEAP_DUMP_INFO tag to change heaps. - rec->AddU1(HPROF_HEAP_DUMP_INFO); - rec->AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type + __ AddU1(HPROF_HEAP_DUMP_INFO); + __ AddU4(static_cast<uint32_t>(heap_type)); // uint32_t: heap type switch (heap_type) { case HPROF_HEAP_APP: nameId = LookupStringId("app"); @@ -844,179 +937,195 @@ int Hprof::DumpHeapObject(mirror::Object* obj) { nameId = LookupStringId("<ILLEGAL>"); break; } - rec->AddStringId(nameId); + __ AddStringId(nameId); current_heap_ = heap_type; } mirror::Class* c = obj->GetClass(); - if (c == NULL) { + if (c == nullptr) { // This object will bother HprofReader, because it has a NULL // class, so just don't dump it. It could be // gDvm.unlinkedJavaLangClass or it could be an object just // allocated which hasn't been initialized yet. } else { if (obj->IsClass()) { - mirror::Class* thisClass = obj->AsClass(); - // obj is a ClassObject. - size_t sFieldCount = thisClass->NumStaticFields(); - if (sFieldCount != 0) { - int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed - // Create a byte array to reflect the allocation of the - // StaticField array at the end of this class. - rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); - rec->AddClassStaticsId(thisClass); - rec->AddU4(StackTraceSerialNumber(obj)); - rec->AddU4(byteLength); - rec->AddU1(hprof_basic_byte); - for (int i = 0; i < byteLength; ++i) { - rec->AddU1(0); - } - } + DumpHeapClass(obj->AsClass(), output); + } else if (c->IsArrayClass()) { + DumpHeapArray(obj->AsArray(), c, output); + } else { + DumpHeapInstanceObject(obj, c, output); + } + } - rec->AddU1(HPROF_CLASS_DUMP); - rec->AddClassId(LookupClassId(thisClass)); - rec->AddU4(StackTraceSerialNumber(thisClass)); - rec->AddClassId(LookupClassId(thisClass->GetSuperClass())); - rec->AddObjectId(thisClass->GetClassLoader()); - rec->AddObjectId(nullptr); // no signer - rec->AddObjectId(nullptr); // no prot domain - rec->AddObjectId(nullptr); // reserved - rec->AddObjectId(nullptr); // reserved - if (thisClass->IsClassClass()) { - // ClassObjects have their static fields appended, so aren't all the same size. - // But they're at least this size. - rec->AddU4(sizeof(mirror::Class)); // instance size - } else if (thisClass->IsArrayClass() || thisClass->IsPrimitive()) { - rec->AddU4(0); - } else { - rec->AddU4(thisClass->GetObjectSize()); // instance size - } + ++objects_in_segment_; +} - rec->AddU2(0); // empty const pool +void Hprof::DumpHeapClass(mirror::Class* klass, EndianOutput* output) { + size_t sFieldCount = klass->NumStaticFields(); + if (sFieldCount != 0) { + int byteLength = sFieldCount * sizeof(JValue); // TODO bogus; fields are packed + // Create a byte array to reflect the allocation of the + // StaticField array at the end of this class. + __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); + __ AddClassStaticsId(klass); + __ AddU4(StackTraceSerialNumber(klass)); + __ AddU4(byteLength); + __ AddU1(hprof_basic_byte); + for (int i = 0; i < byteLength; ++i) { + __ AddU1(0); + } + } - // Static fields - if (sFieldCount == 0) { - rec->AddU2((uint16_t)0); - } else { - rec->AddU2((uint16_t)(sFieldCount+1)); - rec->AddStringId(LookupStringId(STATIC_OVERHEAD_NAME)); - rec->AddU1(hprof_basic_object); - rec->AddClassStaticsId(thisClass); - - for (size_t i = 0; i < sFieldCount; ++i) { - mirror::ArtField* f = thisClass->GetStaticField(i); - - size_t size; - HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); - rec->AddStringId(LookupStringId(f->GetName())); - rec->AddU1(t); - if (size == 1) { - rec->AddU1(static_cast<uint8_t>(f->Get32(thisClass))); - } else if (size == 2) { - rec->AddU2(static_cast<uint16_t>(f->Get32(thisClass))); - } else if (size == 4) { - rec->AddU4(f->Get32(thisClass)); - } else if (size == 8) { - rec->AddU8(f->Get64(thisClass)); - } else { - CHECK(false); - } - } - } + __ AddU1(HPROF_CLASS_DUMP); + __ AddClassId(LookupClassId(klass)); + __ AddU4(StackTraceSerialNumber(klass)); + __ AddClassId(LookupClassId(klass->GetSuperClass())); + __ AddObjectId(klass->GetClassLoader()); + __ AddObjectId(nullptr); // no signer + __ AddObjectId(nullptr); // no prot domain + __ AddObjectId(nullptr); // reserved + __ AddObjectId(nullptr); // reserved + if (klass->IsClassClass()) { + // ClassObjects have their static fields appended, so aren't all the same size. + // But they're at least this size. + __ AddU4(sizeof(mirror::Class)); // instance size + } else if (klass->IsArrayClass() || klass->IsPrimitive()) { + __ AddU4(0); + } else { + __ AddU4(klass->GetObjectSize()); // instance size + } + + __ AddU2(0); // empty const pool - // Instance fields for this class (no superclass fields) - int iFieldCount = thisClass->IsObjectClass() ? 0 : thisClass->NumInstanceFields(); - rec->AddU2((uint16_t)iFieldCount); - for (int i = 0; i < iFieldCount; ++i) { - mirror::ArtField* f = thisClass->GetInstanceField(i); - HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), NULL); - rec->AddStringId(LookupStringId(f->GetName())); - rec->AddU1(t); + // Static fields + if (sFieldCount == 0) { + __ AddU2((uint16_t)0); + } else { + __ AddU2((uint16_t)(sFieldCount+1)); + __ AddStringId(LookupStringId(kStaticOverheadName)); + __ AddU1(hprof_basic_object); + __ AddClassStaticsId(klass); + + for (size_t i = 0; i < sFieldCount; ++i) { + mirror::ArtField* f = klass->GetStaticField(i); + + size_t size; + HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); + __ AddStringId(LookupStringId(f->GetName())); + __ AddU1(t); + switch (size) { + case 1: + __ AddU1(static_cast<uint8_t>(f->Get32(klass))); + break; + case 2: + __ AddU2(static_cast<uint16_t>(f->Get32(klass))); + break; + case 4: + __ AddU4(f->Get32(klass)); + break; + case 8: + __ AddU8(f->Get64(klass)); + break; + default: + LOG(FATAL) << "Unexpected size " << size; + UNREACHABLE(); } - } else if (c->IsArrayClass()) { - mirror::Array* aobj = obj->AsArray(); - uint32_t length = aobj->GetLength(); + } + } + + // Instance fields for this class (no superclass fields) + int iFieldCount = klass->IsObjectClass() ? 0 : klass->NumInstanceFields(); + __ AddU2((uint16_t)iFieldCount); + for (int i = 0; i < iFieldCount; ++i) { + mirror::ArtField* f = klass->GetInstanceField(i); + __ AddStringId(LookupStringId(f->GetName())); + HprofBasicType t = SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), nullptr); + __ AddU1(t); + } +} + +void Hprof::DumpHeapArray(mirror::Array* obj, mirror::Class* klass, EndianOutput* output) { + uint32_t length = obj->GetLength(); - if (obj->IsObjectArray()) { - // obj is an object array. - rec->AddU1(HPROF_OBJECT_ARRAY_DUMP); + if (obj->IsObjectArray()) { + // obj is an object array. + __ AddU1(HPROF_OBJECT_ARRAY_DUMP); - rec->AddObjectId(obj); - rec->AddU4(StackTraceSerialNumber(obj)); - rec->AddU4(length); - rec->AddClassId(LookupClassId(c)); + __ AddObjectId(obj); + __ AddU4(StackTraceSerialNumber(obj)); + __ AddU4(length); + __ AddClassId(LookupClassId(klass)); - // Dump the elements, which are always objects or NULL. - rec->AddIdList(aobj->AsObjectArray<mirror::Object>()); + // Dump the elements, which are always objects or NULL. + __ AddIdList(obj->AsObjectArray<mirror::Object>()); + } else { + size_t size; + HprofBasicType t = SignatureToBasicTypeAndSize( + Primitive::Descriptor(klass->GetComponentType()->GetPrimitiveType()), &size); + + // obj is a primitive array. + __ AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); + + __ AddObjectId(obj); + __ AddU4(StackTraceSerialNumber(obj)); + __ AddU4(length); + __ AddU1(t); + + // Dump the raw, packed element values. + if (size == 1) { + __ AddU1List(reinterpret_cast<const uint8_t*>(obj->GetRawData(sizeof(uint8_t), 0)), length); + } else if (size == 2) { + __ AddU2List(reinterpret_cast<const uint16_t*>(obj->GetRawData(sizeof(uint16_t), 0)), length); + } else if (size == 4) { + __ AddU4List(reinterpret_cast<const uint32_t*>(obj->GetRawData(sizeof(uint32_t), 0)), length); + } else if (size == 8) { + __ AddU8List(reinterpret_cast<const uint64_t*>(obj->GetRawData(sizeof(uint64_t), 0)), length); + } + } +} + +void Hprof::DumpHeapInstanceObject(mirror::Object* obj, mirror::Class* klass, + EndianOutput* output) { + // obj is an instance object. + __ AddU1(HPROF_INSTANCE_DUMP); + __ AddObjectId(obj); + __ AddU4(StackTraceSerialNumber(obj)); + __ AddClassId(LookupClassId(klass)); + + // Reserve some space for the length of the instance data, which we won't + // know until we're done writing it. + size_t size_patch_offset = output->Length(); + __ AddU4(0x77777777); + + // Write the instance data; fields for this class, followed by super class fields, + // and so on. Don't write the klass or monitor fields of Object.class. + while (!klass->IsObjectClass()) { + int ifieldCount = klass->NumInstanceFields(); + for (int i = 0; i < ifieldCount; ++i) { + mirror::ArtField* f = klass->GetInstanceField(i); + size_t size; + SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); + if (size == 1) { + __ AddU1(f->Get32(obj)); + } else if (size == 2) { + __ AddU2(f->Get32(obj)); + } else if (size == 4) { + __ AddU4(f->Get32(obj)); } else { - size_t size; - HprofBasicType t = PrimitiveToBasicTypeAndSize(c->GetComponentType()->GetPrimitiveType(), &size); - - // obj is a primitive array. - rec->AddU1(HPROF_PRIMITIVE_ARRAY_DUMP); - - rec->AddObjectId(obj); - rec->AddU4(StackTraceSerialNumber(obj)); - rec->AddU4(length); - rec->AddU1(t); - - // Dump the raw, packed element values. - if (size == 1) { - rec->AddU1List((const uint8_t*)aobj->GetRawData(sizeof(uint8_t), 0), length); - } else if (size == 2) { - rec->AddU2List((const uint16_t*)aobj->GetRawData(sizeof(uint16_t), 0), length); - } else if (size == 4) { - rec->AddU4List((const uint32_t*)aobj->GetRawData(sizeof(uint32_t), 0), length); - } else if (size == 8) { - rec->AddU8List((const uint64_t*)aobj->GetRawData(sizeof(uint64_t), 0), length); - } - } - } else { - // obj is an instance object. - rec->AddU1(HPROF_INSTANCE_DUMP); - rec->AddObjectId(obj); - rec->AddU4(StackTraceSerialNumber(obj)); - rec->AddClassId(LookupClassId(c)); - - // Reserve some space for the length of the instance data, which we won't - // know until we're done writing it. - size_t size_patch_offset = rec->Size(); - rec->AddU4(0x77777777); - - // Write the instance data; fields for this class, followed by super class fields, - // and so on. Don't write the klass or monitor fields of Object.class. - mirror::Class* sclass = c; - while (!sclass->IsObjectClass()) { - int ifieldCount = sclass->NumInstanceFields(); - for (int i = 0; i < ifieldCount; ++i) { - mirror::ArtField* f = sclass->GetInstanceField(i); - size_t size; - SignatureToBasicTypeAndSize(f->GetTypeDescriptor(), &size); - if (size == 1) { - rec->AddU1(f->Get32(obj)); - } else if (size == 2) { - rec->AddU2(f->Get32(obj)); - } else if (size == 4) { - rec->AddU4(f->Get32(obj)); - } else { - CHECK_EQ(size, 8U); - rec->AddU8(f->Get64(obj)); - } - } - - sclass = sclass->GetSuperClass(); + CHECK_EQ(size, 8U); + __ AddU8(f->Get64(obj)); } - - // Patch the instance field length. - rec->UpdateU4(size_patch_offset, rec->Size() - (size_patch_offset + 4)); } + + klass = klass->GetSuperClass(); } - ++objects_in_segment_; - return 0; + // Patch the instance field length. + __ UpdateU4(size_patch_offset, output->Length() - (size_patch_offset + 4)); } -void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type) { +void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType type, + EndianOutput* output) { static const HprofHeapTag xlate[] = { HPROF_ROOT_UNKNOWN, HPROF_ROOT_JNI_GLOBAL, @@ -1035,14 +1144,10 @@ void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType ty HPROF_ROOT_JNI_MONITOR, }; CHECK_LT(type, sizeof(xlate) / sizeof(HprofHeapTag)); - if (obj == NULL) { + if (obj == nullptr) { return; } - gc_scan_state_ = xlate[type]; - gc_thread_serial_number_ = thread_id; - MarkRootObject(obj, 0); - gc_scan_state_ = 0; - gc_thread_serial_number_ = 0; + MarkRootObject(obj, 0, xlate[type], thread_id, output); } // If "direct_to_ddms" is true, the other arguments are ignored, and data is @@ -1050,7 +1155,7 @@ void Hprof::VisitRoot(const mirror::Object* obj, uint32_t thread_id, RootType ty // If "fd" is >= 0, the output will be written to that file descriptor. // Otherwise, "filename" is used to create an output file. void DumpHeap(const char* filename, int fd, bool direct_to_ddms) { - CHECK(filename != NULL); + CHECK(filename != nullptr); Runtime::Current()->GetThreadList()->SuspendAll(); Hprof hprof(filename, fd, direct_to_ddms); @@ -1058,78 +1163,5 @@ void DumpHeap(const char* filename, int fd, bool direct_to_ddms) { Runtime::Current()->GetThreadList()->ResumeAll(); } -// Returns how many characters were in the buffer (or written). -size_t HprofRecord::Flush() { - size_t chars = 0; - if (dirty_) { - unsigned char headBuf[sizeof(uint8_t) + 2 * sizeof(uint32_t)]; - headBuf[0] = tag_; - U4_TO_BUF_BE(headBuf, 1, time_); - U4_TO_BUF_BE(headBuf, 5, length_); - chars += hprof_->Write(headBuf, sizeof(headBuf), fp_); - chars += hprof_->Write(body_, length_, fp_); - dirty_ = false; - } - return chars; -} - -void HprofRecord::AddU1(uint8_t value) { - if (hprof_->AllowWriting()) { - GuaranteeRecordAppend(1); - body_[length_] = value; - } - ++length_; -} - -void HprofRecord::AddU1List(const uint8_t* values, size_t numValues) { - if (hprof_->AllowWriting()) { - GuaranteeRecordAppend(numValues); - memcpy(body_ + length_, values, numValues); - } - length_ += numValues; -} - -void HprofRecord::AddU2List(const uint16_t* values, size_t numValues) { - if (hprof_->AllowWriting()) { - GuaranteeRecordAppend(numValues * 2); - unsigned char* insert = body_ + length_; - for (size_t i = 0; i < numValues; ++i) { - U2_TO_BUF_BE(insert, 0, *values++); - insert += sizeof(*values); - } - } - length_ += numValues * 2; -} - -void HprofRecord::AddU4List(const uint32_t* values, size_t numValues) { - if (hprof_->AllowWriting()) { - GuaranteeRecordAppend(numValues * 4); - unsigned char* insert = body_ + length_; - for (size_t i = 0; i < numValues; ++i) { - U4_TO_BUF_BE(insert, 0, *values++); - insert += sizeof(*values); - } - } - length_ += numValues * 4; -} - -void HprofRecord::UpdateU4(size_t offset, uint32_t new_value) { - if (hprof_->AllowWriting()) { - U4_TO_BUF_BE(body_, offset, new_value); - } -} - -void HprofRecord::AddU8List(const uint64_t* values, size_t numValues) { - if (hprof_->AllowWriting()) { - GuaranteeRecordAppend(numValues * 8); - unsigned char* insert = body_ + length_; - for (size_t i = 0; i < numValues; ++i) { - U8_TO_BUF_BE(insert, 0, *values++); - insert += sizeof(*values); - } - } - length_ += numValues * 8; -} - } // namespace hprof } // namespace art diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index b294e480b8..a1d2a6c4bf 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -333,15 +333,15 @@ static JdwpError VM_ClassPaths(JdwpState*, Request*, ExpandBuf* pReply) std::vector<std::string> class_path; Split(Runtime::Current()->GetClassPathString(), ':', &class_path); expandBufAdd4BE(pReply, class_path.size()); - for (size_t i = 0; i < class_path.size(); ++i) { - expandBufAddUtf8String(pReply, class_path[i]); + for (const std::string& str : class_path) { + expandBufAddUtf8String(pReply, str); } std::vector<std::string> boot_class_path; Split(Runtime::Current()->GetBootClassPathString(), ':', &boot_class_path); expandBufAdd4BE(pReply, boot_class_path.size()); - for (size_t i = 0; i < boot_class_path.size(); ++i) { - expandBufAddUtf8String(pReply, boot_class_path[i]); + for (const std::string& str : boot_class_path) { + expandBufAddUtf8String(pReply, str); } return ERR_NONE; diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 20db368c21..e415c3d1cd 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -104,7 +104,20 @@ bool ObjectRegistry::ContainsLocked(Thread* self, mirror::Object* o, int32_t ide } void ObjectRegistry::Clear() { - Thread* self = Thread::Current(); + Thread* const self = Thread::Current(); + + // We must not hold the mutator lock exclusively if we want to delete weak global + // references. Otherwise this can lead to a deadlock with a running GC: + // 1. GC thread disables access to weak global references, then releases + // mutator lock. + // 2. JDWP thread takes mutator lock exclusively after suspending all + // threads. + // 3. GC thread waits for shared mutator lock which is held by JDWP + // thread. + // 4. JDWP thread clears weak global references but need to wait for GC + // thread to re-enable access to them. + Locks::mutator_lock_->AssertNotExclusiveHeld(self); + MutexLock mu(self, lock_); VLOG(jdwp) << "Object registry contained " << object_to_entry_.size() << " entries"; // Delete all the JNI references. diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 4ba3cb999d..efc4a7195b 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -413,6 +413,12 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize use_homogeneous_space_compaction_for_oom_ = true; } else if (option == "-XX:DisableHSpaceCompactForOOM") { use_homogeneous_space_compaction_for_oom_ = false; + } else if (StartsWith(option, "-XX:HspaceCompactForOOMMinIntervalMs=")) { + unsigned int value; + if (!ParseUnsignedInteger(option, '=', &value)) { + return false; + } + min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(value); } else if (StartsWith(option, "-D")) { properties_.push_back(option.substr(strlen("-D"))); } else if (StartsWith(option, "-Xjnitrace:")) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index fb6034dcd3..c2e814bffe 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -865,6 +865,16 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) if (kIsDebugBuild) { GetHeap()->GetImageSpace()->VerifyImageAllocations(); } + if (boot_class_path_string_.empty()) { + // The bootclasspath is not explicitly specified: construct it from the loaded dex files. + const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath(); + std::vector<std::string> dex_locations; + dex_locations.reserve(boot_class_path.size()); + for (const DexFile* dex_file : boot_class_path) { + dex_locations.push_back(dex_file->GetLocation()); + } + boot_class_path_string_ = Join(dex_locations, ':'); + } } else { std::vector<std::string> dex_filenames; Split(boot_class_path_string_, ':', &dex_filenames); diff --git a/test/130-hprof/expected.txt b/test/130-hprof/expected.txt new file mode 100644 index 0000000000..cc3d9f25c4 --- /dev/null +++ b/test/130-hprof/expected.txt @@ -0,0 +1 @@ +Generated data. diff --git a/test/130-hprof/info.txt b/test/130-hprof/info.txt new file mode 100644 index 0000000000..64475ef2d9 --- /dev/null +++ b/test/130-hprof/info.txt @@ -0,0 +1 @@ +Dump the heap for this test. diff --git a/test/130-hprof/src/Main.java b/test/130-hprof/src/Main.java new file mode 100644 index 0000000000..67e52323dd --- /dev/null +++ b/test/130-hprof/src/Main.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2009 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.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +public class Main { + private static final int TEST_LENGTH = 100; + + private static boolean makeArray(int i) { + return i % 10 == 0; + } + + private static void fillArray(Object global[], Object local[], int i) { + // Very stupid linking. + local[0] = global; + for (int j = 1; j < local.length; j++) { + local[j] = global[j]; + } + } + + public static void main(String[] args) { + // Create some data. + Object data[] = new Object[TEST_LENGTH]; + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + data[i] = new Object[TEST_LENGTH]; + } else { + data[i] = String.valueOf(i); + } + } + for (int i = 0; i < data.length; i++) { + if (makeArray(i)) { + Object data2[] = (Object[]) data[i]; + fillArray(data, data2, i); + } + } + System.out.println("Generated data."); + + File dumpFile = null; + File convFile = null; + + try { + // Now dump the heap. + dumpFile = createDump(); + + // Run hprof-conv on it. + convFile = getConvFile(); + + File hprof_conv = getHprofConf(); + try { + ProcessBuilder pb = new ProcessBuilder( + hprof_conv.getAbsoluteFile().toString(), + dumpFile.getAbsoluteFile().toString(), + convFile.getAbsoluteFile().toString()); + pb.redirectErrorStream(true); + Process process = pb.start(); + int ret = process.waitFor(); + if (ret != 0) { + throw new RuntimeException("Exited abnormally with " + ret); + } + } catch (Exception exc) { + throw new RuntimeException(exc); + } + } finally { + // Delete the files. + if (dumpFile != null) { + dumpFile.delete(); + } + if (convFile != null) { + convFile.delete(); + } + } + } + + private static File getHprofConf() { + // Use the java.library.path. It points to the lib directory. + File libDir = new File(System.getProperty("java.library.path")); + return new File(new File(libDir.getParentFile(), "bin"), "hprof-conv"); + } + + private static File createDump() { + java.lang.reflect.Method dumpHprofDataMethod = getDumpHprofDataMethod(); + if (dumpHprofDataMethod != null) { + File f = getDumpFile(); + try { + dumpHprofDataMethod.invoke(null, f.getAbsoluteFile().toString()); + return f; + } catch (Exception exc) { + exc.printStackTrace(System.out); + } + } else { + System.out.println("Could not find dump method!"); + } + return null; + } + + /** + * Finds VMDebug.dumpHprofData() through reflection. In the reference + * implementation this will not be available. + * + * @return the reflection object, or null if the method can't be found + */ + private static Method getDumpHprofDataMethod() { + ClassLoader myLoader = Main.class.getClassLoader(); + Class vmdClass; + try { + vmdClass = myLoader.loadClass("dalvik.system.VMDebug"); + } catch (ClassNotFoundException cnfe) { + return null; + } + + Method meth; + try { + meth = vmdClass.getMethod("dumpHprofData", + new Class[] { String.class }); + } catch (NoSuchMethodException nsme) { + System.err.println("Found VMDebug but not dumpHprofData method"); + return null; + } + + return meth; + } + + private static File getDumpFile() { + try { + return File.createTempFile("test-130-hprof", "dump"); + } catch (Exception exc) { + return null; + } + } + + private static File getConvFile() { + try { + return File.createTempFile("test-130-hprof", "conv"); + } catch (Exception exc) { + return null; + } + } +} diff --git a/test/439-swap-double/expected.txt b/test/439-swap-double/expected.txt new file mode 100644 index 0000000000..019c90149e --- /dev/null +++ b/test/439-swap-double/expected.txt @@ -0,0 +1,4 @@ +-26.0 +-24.0 +-22.0 +-20.0 diff --git a/test/439-swap-double/info.txt b/test/439-swap-double/info.txt new file mode 100644 index 0000000000..23447d2b12 --- /dev/null +++ b/test/439-swap-double/info.txt @@ -0,0 +1,2 @@ +Test for the optimizing compiler's parallel swap support in +the presence of register pairs (in this case, doubles on ARM). diff --git a/test/439-swap-double/src/Main.java b/test/439-swap-double/src/Main.java new file mode 100644 index 0000000000..da11577e25 --- /dev/null +++ b/test/439-swap-double/src/Main.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test for the optimizing compiler's parallel swap support in +// the presence of register pairs (in this case, doubles on ARM). +public class Main { + public static void main(String[] args) { + new Main().foo(); + } + + public void foo() { + // Do multiple calls to force swapping of registers. Note that + // this depends on the calling convention, as a stack-only convention + // may not need the swapping. + callWithDoubles(a, b, c, d, e, f, g); + callWithDoubles(b, c, d, e, f, g, a); + callWithDoubles(c, d, e, f, g, a, b); + callWithDoubles(d, e, f, g, a, b, c); + } + + public static void callWithDoubles( + double a, double b, double c, double d, double e, double f, double g) { + System.out.println(a - b - c - d - e - f - g); + } + + double a = 1.0; + double b = 2.0; + double c = 3.0; + double d = 4.0; + double e = 5.0; + double f = 6.0; + double g = 7.0; +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index fd66a02f32..dc4ec66ce7 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -229,6 +229,14 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUIL $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),115-native-bridge, \ $(ALL_ADDRESS_SIZES)) +# 130-hprof dumps the heap and runs hprof-conv to check whether the file is somewhat readable. This +# is only possible on the host. +# TODO: Turn off all the other combinations, this is more about testing actual ART code. A gtest is +# very hard to write here, as (for a complete test) JDWP must be set up. +ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ + $(PICTEST_TYPES),130-hprof,$(ALL_ADDRESS_SIZES)) + # All these tests check that we have sane behavior if we don't have a patchoat or dex2oat. # Therefore we shouldn't run them in situations where we actually don't have these since they # explicitly test for them. These all also assume we have an image. @@ -580,7 +588,7 @@ define define-test-art-run-test endif $$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options) .PHONY: $$(run_test_rule_name) -$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $$(prereq_rule) +$$(run_test_rule_name): $(DX) $(HOST_OUT_EXECUTABLES)/jasmin $(HOST_OUT_EXECUTABLES)/smali $(HOST_OUT_EXECUTABLES)/dexmerger $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule) $(hide) $$(call ART_TEST_SKIP,$$@) && \ DX=$(abspath $(DX)) JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \ diff --git a/test/run-test b/test/run-test index aba4e05d03..d2b1ec9b05 100755 --- a/test/run-test +++ b/test/run-test @@ -281,7 +281,8 @@ tmp_dir="`cd $oldwd ; python -c "import os; print os.path.realpath('$tmp_dir')"` mkdir -p $tmp_dir if [ "$basic_verify" = "true" ]; then - run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify" + # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests. + run_args="${run_args} --runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0" fi if [ "$gc_verify" = "true" ]; then run_args="${run_args} --runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc" diff --git a/tools/checker.py b/tools/checker.py index a7cde62fe8..406a311638 100755 --- a/tools/checker.py +++ b/tools/checker.py @@ -713,7 +713,7 @@ def CompileTest(inputFile, tempFolder): classFolder = tempFolder + "/classes" dexFile = tempFolder + "/test.dex" oatFile = tempFolder + "/test.oat" - outputFile = tempFolder + "/test.cfg" + outputFile = tempFolder + "/art.cfg" os.makedirs(classFolder) # Build a DEX from the source file. We pass "--no-optimize" to dx to avoid @@ -723,7 +723,7 @@ def CompileTest(inputFile, tempFolder): # Run dex2oat and export the HGraph. The output is stored into ${PWD}/art.cfg. with cd(tempFolder): - check_call(["dex2oat", "-j1", "--dump-cfg=" + outputFile, "--compiler-backend=Optimizing", + check_call(["dex2oat", "-j1", "--dump-passes", "--compiler-backend=Optimizing", "--android-root=" + os.environ["ANDROID_HOST_OUT"], "--boot-image=" + os.environ["ANDROID_HOST_OUT"] + "/framework/core-optimizing.art", "--runtime-arg", "-Xnorelocate", "--dex-file=" + dexFile, "--oat-file=" + oatFile]) |