diff options
64 files changed, 1367 insertions, 599 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index db60ff8bba..4ecda918ed 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -79,6 +79,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/base/histogram_test.cc \ runtime/base/mutex_test.cc \ runtime/base/scoped_flock_test.cc \ + runtime/base/stringprintf_test.cc \ runtime/base/timing_logger_test.cc \ runtime/base/unix_file/fd_file_test.cc \ runtime/base/unix_file/mapped_file_test.cc \ diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc index b265ee7431..9fa5facd19 100644 --- a/compiler/dex/mir_analysis.cc +++ b/compiler/dex/mir_analysis.cc @@ -1094,6 +1094,7 @@ bool MIRGraph::SkipCompilation(std::string* skip_message) { default_cutoff = compiler_options.GetSmallMethodThreshold(); break; case CompilerOptions::kSpeed: + case CompilerOptions::kTime: small_cutoff = compiler_options.GetHugeMethodThreshold(); default_cutoff = compiler_options.GetHugeMethodThreshold(); break; diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 6910e04be1..d743f907a3 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -607,7 +607,7 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const De Resolve(class_loader, dex_files, thread_pool, timings); if (!compiler_options_->IsVerificationEnabled()) { - VLOG(compiler) << "Verify none mode specified, skipping verification."; + LOG(INFO) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, thread_pool, timings); return; } @@ -1796,8 +1796,11 @@ static void SetVerifiedClass(const ParallelCompilationManager* manager, size_t c ClassReference ref(manager->GetDexFile(), class_def_index); manager->GetCompiler()->RecordClassStatus(ref, klass->GetStatus()); } + } else { + Thread* self = soa.Self(); + DCHECK(self->IsExceptionPending()); + self->ClearException(); } - soa.Self()->AssertNoPendingException(); } void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 3c76098109..e7bd35776a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -260,7 +260,7 @@ class CompilerDriver { uint16_t* declaring_class_idx, uint16_t* declaring_method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Get declaration location of a resolved field. + // Get the index in the vtable of the method. uint16_t GetResolvedMethodVTableIndex( mirror::ArtMethod* resolved_method, InvokeType type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h index c0f91d1646..eb3de975d2 100644 --- a/compiler/driver/compiler_options.h +++ b/compiler/driver/compiler_options.h @@ -27,7 +27,8 @@ class CompilerOptions { kSpace, // Maximize space savings. kBalanced, // Try to get the best performance return on compilation investment. kSpeed, // Maximize runtime performance. - kEverything // Force compilation (Note: excludes compilaton of class initializers). + kEverything, // Force compilation (Note: excludes compilation of class initializers). + kTime // Compile methods, but minimize compilation time. }; // Guide heuristics to determine whether to compile method if profile data not available. diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index a03588f4fd..33b00d2ac9 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -331,18 +331,61 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, bool is_range, uint32_t* args, uint32_t register_index) { + Instruction::Code opcode = instruction.Opcode(); + InvokeType invoke_type; + switch (opcode) { + case Instruction::INVOKE_STATIC: + case Instruction::INVOKE_STATIC_RANGE: + invoke_type = kStatic; + break; + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_DIRECT_RANGE: + invoke_type = kDirect; + break; + case Instruction::INVOKE_VIRTUAL: + case Instruction::INVOKE_VIRTUAL_RANGE: + invoke_type = kVirtual; + break; + case Instruction::INVOKE_INTERFACE: + case Instruction::INVOKE_INTERFACE_RANGE: + invoke_type = kInterface; + break; + case Instruction::INVOKE_SUPER_RANGE: + case Instruction::INVOKE_SUPER: + invoke_type = kSuper; + break; + default: + LOG(FATAL) << "Unexpected invoke op: " << opcode; + return false; + } + const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx); const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(method_id.proto_idx_); const char* descriptor = dex_file_->StringDataByIdx(proto_id.shorty_idx_); Primitive::Type return_type = Primitive::GetType(descriptor[0]); - bool is_instance_call = - instruction.Opcode() != Instruction::INVOKE_STATIC - && instruction.Opcode() != Instruction::INVOKE_STATIC_RANGE; + bool is_instance_call = invoke_type != kStatic; const size_t number_of_arguments = strlen(descriptor) - (is_instance_call ? 0 : 1); - // Treat invoke-direct like static calls for now. - HInvoke* invoke = new (arena_) HInvokeStatic( - arena_, number_of_arguments, return_type, dex_offset, method_idx); + HInvoke* invoke = nullptr; + if (invoke_type == kVirtual) { + MethodReference target_method(dex_file_, method_idx); + uintptr_t direct_code; + uintptr_t direct_method; + int vtable_index; + // TODO: Add devirtualization support. + compiler_driver_->ComputeInvokeInfo(dex_compilation_unit_, dex_offset, true, true, + &invoke_type, &target_method, &vtable_index, + &direct_code, &direct_method); + if (vtable_index == -1) { + return false; + } + invoke = new (arena_) HInvokeVirtual( + arena_, number_of_arguments, return_type, dex_offset, vtable_index); + } else { + // Treat invoke-direct like static calls for now. + invoke = new (arena_) HInvokeStatic( + arena_, number_of_arguments, return_type, dex_offset, method_idx); + } size_t start_index = 0; Temporaries temps(graph_, is_instance_call ? 1 : 0); @@ -620,7 +663,8 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::INVOKE_STATIC: - case Instruction::INVOKE_DIRECT: { + case Instruction::INVOKE_DIRECT: + case Instruction::INVOKE_VIRTUAL: { uint32_t method_idx = instruction.VRegB_35c(); uint32_t number_of_vreg_arguments = instruction.VRegA_35c(); uint32_t args[5]; @@ -632,7 +676,8 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 } case Instruction::INVOKE_STATIC_RANGE: - case Instruction::INVOKE_DIRECT_RANGE: { + case Instruction::INVOKE_DIRECT_RANGE: + case Instruction::INVOKE_VIRTUAL_RANGE: { uint32_t method_idx = instruction.VRegB_3rc(); uint32_t number_of_vreg_arguments = instruction.VRegA_3rc(); uint32_t register_index = instruction.VRegC(); diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 99030922a7..ad622798a6 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -20,6 +20,7 @@ #include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.h" +#include "mirror/class.h" #include "thread.h" #include "utils/assembler.h" #include "utils/arm/assembler_arm.h" @@ -818,6 +819,47 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { } void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) { + __ ldr(reg, Address(SP, kCurrentMethodStackOffset)); +} + +void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { + Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister(); + uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); + size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + + invoke->GetIndexInDexCache() * kArmWordSize; + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + // temp = method; + LoadCurrentMethod(temp); + // temp = temp->dex_cache_resolved_methods_; + __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); + // temp = temp[index_in_cache] + __ ldr(temp, Address(temp, index_in_cache)); + // LR = temp[offset_of_quick_compiled_code] + __ ldr(LR, Address(temp, + mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + // LR() + __ blx(LR); + + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + DCHECK(!codegen_->IsLeafMethod()); +} + +void LocationsBuilderARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderARM::HandleInvoke(HInvoke* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); locations->AddTemp(ArmCoreLocation(R0)); @@ -852,37 +894,30 @@ void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { } } -void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) { - __ ldr(reg, Address(SP, kCurrentMethodStackOffset)); -} -void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorARM::VisitInvokeVirtual(HInvokeVirtual* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsArm().AsCoreRegister(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + - invoke->GetIndexInDexCache() * kArmWordSize; - - // TODO: Implement all kinds of calls: - // 1) boot -> boot - // 2) app -> boot - // 3) app -> app - // - // Currently we implement the app -> app logic, which looks up in the resolve cache. - - // temp = method; - LoadCurrentMethod(temp); - // temp = temp->dex_cache_resolved_methods_; - __ ldr(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); - // temp = temp[index_in_cache] - __ ldr(temp, Address(temp, index_in_cache)); - // LR = temp[offset_of_quick_compiled_code] - __ ldr(LR, Address(temp, - mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); - // LR() + uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() + + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ ldr(temp, Address(SP, receiver.GetStackIndex())); + __ ldr(temp, Address(temp, class_offset)); + } else { + __ ldr(temp, Address(receiver.AsArm().AsCoreRegister(), class_offset)); + } + // temp = temp->GetMethodAt(method_offset); + uint32_t entry_point = mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value(); + __ ldr(temp, Address(temp, method_offset)); + // LR = temp->GetEntryPoint(); + __ ldr(LR, Address(temp, entry_point)); + // LR(); __ blx(LR); - - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } void LocationsBuilderARM::VisitAdd(HAdd* add) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 660294b147..2480960f32 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -93,6 +93,8 @@ class LocationsBuilderARM : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION + void HandleInvoke(HInvoke* invoke); + private: CodeGeneratorARM* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 3dd9b37158..3383cb2117 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -20,6 +20,7 @@ #include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.h" +#include "mirror/class.h" #include "thread.h" #include "utils/assembler.h" #include "utils/stack_checks.h" @@ -763,6 +764,40 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { } void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { + Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister(); + uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); + size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + + invoke->GetIndexInDexCache() * kX86WordSize; + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + // temp = method; + LoadCurrentMethod(temp); + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, index_in_cache)); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); + + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderX86::HandleInvoke(HInvoke* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); locations->AddTemp(X86CpuLocation(EAX)); @@ -799,26 +834,23 @@ void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { invoke->SetLocations(locations); } -void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorX86::VisitInvokeVirtual(HInvokeVirtual* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsX86().AsCpuRegister(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() + - invoke->GetIndexInDexCache() * kX86WordSize; - - // TODO: Implement all kinds of calls: - // 1) boot -> boot - // 2) app -> boot - // 3) app -> app - // - // Currently we implement the app -> app logic, which looks up in the resolve cache. - - // temp = method; - LoadCurrentMethod(temp); - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, index_in_cache)); - // (temp + offset_of_quick_compiled_code)() + uint32_t method_offset = mirror::Class::EmbeddedVTableOffset().Uint32Value() + + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + uint32_t class_offset = mirror::Object::ClassOffset().Int32Value(); + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ movl(temp, Address(ESP, receiver.GetStackIndex())); + __ movl(temp, Address(temp, class_offset)); + } else { + __ movl(temp, Address(receiver.AsX86().AsCpuRegister(), class_offset)); + } + // temp = temp->GetMethodAt(method_offset); + __ movl(temp, Address(temp, method_offset)); + // call temp->GetEntryPoint(); __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value())); DCHECK(!codegen_->IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 7c502049d8..f1be0ad5b7 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -94,6 +94,8 @@ class LocationsBuilderX86 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION + void HandleInvoke(HInvoke* invoke); + private: CodeGeneratorX86* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 2f352e0838..ca03af8e9f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -20,6 +20,7 @@ #include "gc/accounting/card_table.h" #include "mirror/array.h" #include "mirror/art_method.h" +#include "mirror/class.h" #include "mirror/object_reference.h" #include "thread.h" #include "utils/assembler.h" @@ -709,12 +710,46 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type } void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { + CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsX86_64().AsCpuRegister(); + uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); + size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).SizeValue() + + invoke->GetIndexInDexCache() * heap_reference_size; + + // TODO: Implement all kinds of calls: + // 1) boot -> boot + // 2) app -> boot + // 3) app -> app + // + // Currently we implement the app -> app logic, which looks up in the resolve cache. + + // temp = method; + LoadCurrentMethod(temp); + // temp = temp->dex_cache_resolved_methods_; + __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); + // temp = temp[index_in_cache] + __ movl(temp, Address(temp, index_in_cache)); + // (temp + offset_of_quick_compiled_code)() + __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); + + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); +} + +void LocationsBuilderX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) { + HandleInvoke(invoke); +} + +void LocationsBuilderX86_64::HandleInvoke(HInvoke* invoke) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(invoke, LocationSummary::kCall); locations->AddTemp(X86_64CpuLocation(RDI)); InvokeDexCallingConventionVisitor calling_convention_visitor; - for (size_t i = 0; i < invoke->InputCount(); ++i) { + for (size_t i = 0; i < invoke->InputCount(); i++) { HInstruction* input = invoke->InputAt(i); locations->SetInAt(i, calling_convention_visitor.GetNextLocation(input->GetType())); } @@ -740,26 +775,23 @@ void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { } } -void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorX86_64::VisitInvokeVirtual(HInvokeVirtual* invoke) { CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsX86_64().AsCpuRegister(); - uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>); - size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).SizeValue() + - invoke->GetIndexInDexCache() * heap_reference_size; - - // TODO: Implement all kinds of calls: - // 1) boot -> boot - // 2) app -> boot - // 3) app -> app - // - // Currently we implement the app -> app logic, which looks up in the resolve cache. - - // temp = method; - LoadCurrentMethod(temp); - // temp = temp->dex_cache_resolved_methods_; - __ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue())); - // temp = temp[index_in_cache] - __ movl(temp, Address(temp, index_in_cache)); - // (temp + offset_of_quick_compiled_code)() + size_t method_offset = mirror::Class::EmbeddedVTableOffset().SizeValue() + + invoke->GetVTableIndex() * sizeof(mirror::Class::VTableEntry); + LocationSummary* locations = invoke->GetLocations(); + Location receiver = locations->InAt(0); + size_t class_offset = mirror::Object::ClassOffset().SizeValue(); + // temp = object->GetClass(); + if (receiver.IsStackSlot()) { + __ movq(temp, Address(CpuRegister(RSP), receiver.GetStackIndex())); + __ movq(temp, Address(temp, class_offset)); + } else { + __ movq(temp, Address(receiver.AsX86_64().AsCpuRegister(), class_offset)); + } + // temp = temp->GetMethodAt(method_offset); + __ movl(temp, Address(temp, method_offset)); + // call temp->GetEntryPoint(); __ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue())); DCHECK(!codegen_->IsLeafMethod()); diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 44552ea465..78b60fe93c 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -91,6 +91,8 @@ class LocationsBuilderX86_64 : public HGraphVisitor { #undef DECLARE_VISIT_INSTRUCTION + void HandleInvoke(HInvoke* invoke); + private: CodeGeneratorX86_64* const codegen_; InvokeDexCallingConventionVisitor parameter_visitor_; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ed6dd939de..d6dfeaede8 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -422,6 +422,7 @@ class HBasicBlock : public ArenaObject { M(If) \ M(IntConstant) \ M(InvokeStatic) \ + M(InvokeVirtual) \ M(LoadLocal) \ M(Local) \ M(LongConstant) \ @@ -1272,6 +1273,26 @@ class HInvokeStatic : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeStatic); }; +class HInvokeVirtual : public HInvoke { + public: + HInvokeVirtual(ArenaAllocator* arena, + uint32_t number_of_arguments, + Primitive::Type return_type, + uint32_t dex_pc, + uint32_t vtable_index) + : HInvoke(arena, number_of_arguments, return_type, dex_pc), + vtable_index_(vtable_index) {} + + uint32_t GetVTableIndex() const { return vtable_index_; } + + DECLARE_INSTRUCTION(InvokeVirtual); + + private: + const uint32_t vtable_index_; + + DISALLOW_COPY_AND_ASSIGN(HInvokeVirtual); +}; + class HNewInstance : public HExpression<0> { public: HNewInstance(uint32_t dex_pc, uint16_t type_index) diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 75f41557e2..a53919268f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -38,7 +38,7 @@ namespace art { */ class CodeVectorAllocator FINAL : public CodeAllocator { public: - CodeVectorAllocator() { } + CodeVectorAllocator() {} virtual uint8_t* Allocate(size_t size) { size_ = size; @@ -70,6 +70,7 @@ static const char* kStringFilter = ""; class OptimizingCompiler FINAL : public Compiler { public: explicit OptimizingCompiler(CompilerDriver* driver); + ~OptimizingCompiler(); bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const OVERRIDE; @@ -113,6 +114,13 @@ class OptimizingCompiler FINAL : public Compiler { void UnInit() const OVERRIDE; private: + // Whether we should run any optimization or register allocation. If false, will + // just run the code generation after the graph was built. + const bool run_optimizations_; + mutable AtomicInteger total_compiled_methods_; + mutable AtomicInteger unoptimized_compiled_methods_; + mutable AtomicInteger optimized_compiled_methods_; + std::unique_ptr<std::ostream> visualizer_output_; // Delegate to another compiler in case the optimizing compiler cannot compile a method. @@ -122,8 +130,16 @@ class OptimizingCompiler FINAL : public Compiler { DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler); }; -OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) : Compiler(driver, 100), - delegate_(Create(driver, Compiler::Kind::kQuick)) { +static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */ + +OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver) + : Compiler(driver, kMaximumCompilationTimeBeforeWarning), + run_optimizations_( + driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime), + total_compiled_methods_(0), + unoptimized_compiled_methods_(0), + optimized_compiled_methods_(0), + delegate_(Create(driver, Compiler::Kind::kQuick)) { if (kIsVisualizerEnabled) { visualizer_output_.reset(new std::ofstream("art.cfg")); } @@ -137,6 +153,14 @@ void OptimizingCompiler::UnInit() const { delegate_->UnInit(); } +OptimizingCompiler::~OptimizingCompiler() { + size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_); + size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_); + LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: " + << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, " + << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized."; +} + bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const { return delegate_->CanCompileMethod(method_idx, dex_file, cu); @@ -173,6 +197,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite uint32_t method_idx, jobject class_loader, const DexFile& dex_file) const { + total_compiled_methods_++; InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet(); // Always use the thumb2 assembler: some runtime functionality (like implicit stack // overflow checks) assume thumb2. @@ -222,7 +247,8 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite CodeVectorAllocator allocator; - if (RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) { + if (run_optimizations_ && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) { + optimized_compiled_methods_++; graph->BuildDominatorTree(); graph->TransformToSSA(); visualizer.DumpGraph("ssa"); @@ -262,6 +288,7 @@ CompiledMethod* OptimizingCompiler::TryCompile(const DexFile::CodeItem* code_ite LOG(FATAL) << "Could not allocate registers in optimizing compiler"; return nullptr; } else { + unoptimized_compiled_methods_++; codegen->CompileBaseline(&allocator); // Run these phases to get some test coverage. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index be6f09794f..afc01dce2e 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -160,7 +160,14 @@ static void Usage(const char* fmt, ...) { UsageError(" Example: --compiler-backend=Portable"); UsageError(" Default: Quick"); UsageError(""); - UsageError(" --compiler-filter=(verify-none|interpret-only|space|balanced|speed|everything):"); + UsageError(" --compiler-filter=" + "(verify-none" + "|interpret-only" + "|space" + "|balanced" + "|speed" + "|everything" + "|time):"); UsageError(" select compiler filter."); UsageError(" Example: --compiler-filter=everything"); #if ART_SMALL_MODE @@ -435,6 +442,11 @@ class Dex2Oat { return nullptr; } + // Flush result to disk. Patching code will re-open the file (mmap), so ensure that our view + // of the file already made it there and won't be re-ordered with writes from PatchOat or + // image patching. + oat_file->Flush(); + if (!driver->IsImage() && driver->GetCompilerOptions().GetIncludePatchInformation()) { t2.NewTiming("Patching ELF"); std::string error_msg; @@ -1181,6 +1193,8 @@ static int dex2oat(int argc, char** argv) { compiler_filter = CompilerOptions::kSpeed; } else if (strcmp(compiler_filter_string, "everything") == 0) { compiler_filter = CompilerOptions::kEverything; + } else if (strcmp(compiler_filter_string, "time") == 0) { + compiler_filter = CompilerOptions::kTime; } else { Usage("Unknown --compiler-filter value %s", compiler_filter_string); } @@ -1376,7 +1390,7 @@ static int dex2oat(int argc, char** argv) { * If we're not in interpret-only or verify-none mode, go ahead and compile small applications. * Don't bother to check if we're doing the image. */ - if (!image && compiler_options->IsCompilationEnabled()) { + if (!image && compiler_options->IsCompilationEnabled() && compiler_kind == Compiler::kQuick) { size_t num_methods = 0; for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; diff --git a/disassembler/disassembler.cc b/disassembler/disassembler.cc index 41ee21365e..c97bf64c5d 100644 --- a/disassembler/disassembler.cc +++ b/disassembler/disassembler.cc @@ -19,6 +19,7 @@ #include <iostream> #include "base/logging.h" +#include "base/stringprintf.h" #include "disassembler_arm.h" #include "disassembler_arm64.h" #include "disassembler_mips.h" @@ -26,21 +27,30 @@ namespace art { -Disassembler* Disassembler::Create(InstructionSet instruction_set) { +Disassembler* Disassembler::Create(InstructionSet instruction_set, DisassemblerOptions* options) { if (instruction_set == kArm || instruction_set == kThumb2) { - return new arm::DisassemblerArm(); + return new arm::DisassemblerArm(options); } else if (instruction_set == kArm64) { - return new arm64::DisassemblerArm64(); + return new arm64::DisassemblerArm64(options); } else if (instruction_set == kMips) { - return new mips::DisassemblerMips(); + return new mips::DisassemblerMips(options); } else if (instruction_set == kX86) { - return new x86::DisassemblerX86(false); + return new x86::DisassemblerX86(options, false); } else if (instruction_set == kX86_64) { - return new x86::DisassemblerX86(true); + return new x86::DisassemblerX86(options, true); } else { UNIMPLEMENTED(FATAL) << "no disassembler for " << instruction_set; return NULL; } } +std::string Disassembler::FormatInstructionPointer(const uint8_t* begin) { + if (disassembler_options_->absolute_addresses_) { + return StringPrintf("%p", begin); + } else { + size_t offset = begin - disassembler_options_->base_address_; + return StringPrintf("0x%08zx", offset); + } +} + } // namespace art diff --git a/disassembler/disassembler.h b/disassembler/disassembler.h index 183e692ff2..487f43315a 100644 --- a/disassembler/disassembler.h +++ b/disassembler/disassembler.h @@ -26,10 +26,31 @@ namespace art { +class DisassemblerOptions { + public: + // Should the disassembler print absolute or relative addresses. + const bool absolute_addresses_; + + // Base addess for calculating relative code offsets when absolute_addresses_ is false. + const uint8_t* const base_address_; + + DisassemblerOptions(bool absolute_addresses, const uint8_t* base_address) + : absolute_addresses_(absolute_addresses), base_address_(base_address) {} + + private: + DISALLOW_COPY_AND_ASSIGN(DisassemblerOptions); +}; + class Disassembler { public: - static Disassembler* Create(InstructionSet instruction_set); - virtual ~Disassembler() {} + // Creates a Disassembler for the given InstructionSet with the + // non-null DisassemblerOptions which become owned by the + // Disassembler. + static Disassembler* Create(InstructionSet instruction_set, DisassemblerOptions* options); + + virtual ~Disassembler() { + delete disassembler_options_; + } // Dump a single instruction returning the length of that instruction. virtual size_t Dump(std::ostream& os, const uint8_t* begin) = 0; @@ -37,9 +58,15 @@ class Disassembler { virtual void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) = 0; protected: - Disassembler() {} + explicit Disassembler(DisassemblerOptions* disassembler_options) + : disassembler_options_(disassembler_options) { + CHECK(disassembler_options_ != nullptr); + } + + std::string FormatInstructionPointer(const uint8_t* begin); private: + DisassemblerOptions* disassembler_options_; DISALLOW_COPY_AND_ASSIGN(Disassembler); }; diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 56023c1595..54e77612a3 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -94,7 +94,7 @@ void DisassemblerArm::DumpMemoryDomain(std::ostream& os, uint32_t domain) { } void DisassemblerArm::DumpBranchTarget(std::ostream& os, const uint8_t* instr_ptr, int32_t imm32) { - os << StringPrintf("%+d (%p)", imm32, instr_ptr + imm32); + os << StringPrintf("%+d (", imm32) << FormatInstructionPointer(instr_ptr + imm32) << ")"; } static uint32_t ReadU16(const uint8_t* ptr) { @@ -356,7 +356,9 @@ void DisassemblerArm::DumpArm(std::ostream& os, const uint8_t* instr_ptr) { opcode += kConditionCodeNames[cond]; opcode += suffixes; // TODO: a more complete ARM disassembler could generate wider opcodes. - os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n'; + os << FormatInstructionPointer(instr_ptr) + << StringPrintf(": %08x\t%-7s ", instruction, opcode.c_str()) + << args.str() << '\n'; } int32_t ThumbExpand(int32_t imm12) { @@ -1608,7 +1610,9 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) opcode << "UNKNOWN " << op2; } - os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n'; + os << FormatInstructionPointer(instr_ptr) + << StringPrintf(": %08x\t%-7s ", instr, opcode.str().c_str()) + << args.str() << '\n'; return 4; } // NOLINT(readability/fn_size) @@ -1936,7 +1940,9 @@ size_t DisassemblerArm::DumpThumb16(std::ostream& os, const uint8_t* instr_ptr) it_conditions_.pop_back(); } - os << StringPrintf("%p: %04x \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n'; + os << FormatInstructionPointer(instr_ptr) + << StringPrintf(": %04x \t%-7s ", instr, opcode.str().c_str()) + << args.str() << '\n'; } return 2; } diff --git a/disassembler/disassembler_arm.h b/disassembler/disassembler_arm.h index f6d7fdad52..f870e8ef86 100644 --- a/disassembler/disassembler_arm.h +++ b/disassembler/disassembler_arm.h @@ -26,8 +26,7 @@ namespace arm { class DisassemblerArm FINAL : public Disassembler { public: - DisassemblerArm() { - } + explicit DisassemblerArm(DisassemblerOptions* options) : Disassembler(options) {} size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE; void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE; diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc index 864d22da76..5d0c2189cc 100644 --- a/disassembler/disassembler_arm64.cc +++ b/disassembler/disassembler_arm64.cc @@ -34,7 +34,8 @@ static uint32_t ReadU32(const uint8_t* ptr) { size_t DisassemblerArm64::Dump(std::ostream& os, const uint8_t* begin) { uint32_t instruction = ReadU32(begin); decoder.Decode(reinterpret_cast<vixl::Instruction*>(&instruction)); - os << StringPrintf("%p: %08x\t%s\n", begin, instruction, disasm.GetOutput()); + os << FormatInstructionPointer(begin) + << StringPrintf(": %08x\t%s\n", instruction, disasm.GetOutput()); return vixl::kInstructionSize; } diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h index 28c0fa7556..ad20c70222 100644 --- a/disassembler/disassembler_arm64.h +++ b/disassembler/disassembler_arm64.h @@ -27,7 +27,7 @@ namespace arm64 { class DisassemblerArm64 FINAL : public Disassembler { public: - DisassemblerArm64() { + explicit DisassemblerArm64(DisassemblerOptions* options) : Disassembler(options) { decoder.AppendVisitor(&disasm); } diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc index 5e89f6f56c..bd5fac7377 100644 --- a/disassembler/disassembler_mips.cc +++ b/disassembler/disassembler_mips.cc @@ -168,7 +168,7 @@ static uint32_t ReadU32(const uint8_t* ptr) { return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); } -static void DumpMips(std::ostream& os, const uint8_t* instr_ptr) { +size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* instr_ptr) { uint32_t instruction = ReadU32(instr_ptr); uint32_t rs = (instruction >> 21) & 0x1f; // I-type, R-type. @@ -197,7 +197,8 @@ static void DumpMips(std::ostream& os, const uint8_t* instr_ptr) { int32_t offset = static_cast<int16_t>(instruction & 0xffff); offset <<= 2; offset += 4; // Delay slot. - args << StringPrintf("%p ; %+d", instr_ptr + offset, offset); + args << FormatInstructionPointer(instr_ptr + offset) + << StringPrintf(" ; %+d", offset); } break; case 'D': args << 'r' << rd; break; @@ -254,17 +255,15 @@ static void DumpMips(std::ostream& os, const uint8_t* instr_ptr) { } } - os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n'; -} - -size_t DisassemblerMips::Dump(std::ostream& os, const uint8_t* begin) { - DumpMips(os, begin); + os << FormatInstructionPointer(instr_ptr) + << StringPrintf(": %08x\t%-7s ", instruction, opcode.c_str()) + << args.str() << '\n'; return 4; } void DisassemblerMips::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) { for (const uint8_t* cur = begin; cur < end; cur += 4) { - DumpMips(os, cur); + Dump(os, cur); } } diff --git a/disassembler/disassembler_mips.h b/disassembler/disassembler_mips.h index e1fb0340ab..00b2f8dbb4 100644 --- a/disassembler/disassembler_mips.h +++ b/disassembler/disassembler_mips.h @@ -26,8 +26,7 @@ namespace mips { class DisassemblerMips FINAL : public Disassembler { public: - DisassemblerMips() { - } + explicit DisassemblerMips(DisassemblerOptions* options) : Disassembler(options) {} size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE; void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE; diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 1848abe8ab..1d29765d4e 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -1215,7 +1215,9 @@ DISASSEMBLER_ENTRY(cmp, displacement = *reinterpret_cast<const int32_t*>(instr); instr += 4; } - args << StringPrintf("%+d (%p)", displacement, instr + displacement); + args << StringPrintf("%+d (", displacement) + << FormatInstructionPointer(instr + displacement) + << ")"; } if (prefix[1] == kFs && !supports_rex_) { args << " ; "; @@ -1238,8 +1240,8 @@ DISASSEMBLER_ENTRY(cmp, default: LOG(FATAL) << "Unreachable"; } prefixed_opcode << opcode.str(); - os << StringPrintf("%p: %22s \t%-7s ", begin_instr, hex.str().c_str(), - prefixed_opcode.str().c_str()) + os << FormatInstructionPointer(begin_instr) + << StringPrintf(": %22s \t%-7s ", hex.str().c_str(), prefixed_opcode.str().c_str()) << args.str() << '\n'; return instr - begin_instr; } // NOLINT(readability/fn_size) diff --git a/disassembler/disassembler_x86.h b/disassembler/disassembler_x86.h index 2565bb1ee7..f448662f66 100644 --- a/disassembler/disassembler_x86.h +++ b/disassembler/disassembler_x86.h @@ -24,8 +24,8 @@ namespace x86 { class DisassemblerX86 FINAL : public Disassembler { public: - explicit DisassemblerX86(bool supports_rex) : supports_rex_(supports_rex) { - } + DisassemblerX86(DisassemblerOptions* options, bool supports_rex) + : Disassembler(options), supports_rex_(supports_rex) {} size_t Dump(std::ostream& os, const uint8_t* begin) OVERRIDE; void Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) OVERRIDE; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 1cf7757175..bac3c33022 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -79,8 +79,8 @@ static void usage() { " Example: --boot-image=/system/framework/boot.art\n" "\n"); fprintf(stderr, - " --instruction-set=(arm|arm64|mips|x86|x86_64): for locating the image file based on the image location\n" - " set.\n" + " --instruction-set=(arm|arm64|mips|x86|x86_64): for locating the image\n" + " file based on the image location set.\n" " Example: --instruction-set=x86\n" " Default: %s\n" "\n", @@ -90,9 +90,20 @@ static void usage() { " Example: --output=/tmp/oatdump.txt\n" "\n"); fprintf(stderr, - " --dump:[raw_mapping_table|raw_gc_map]\n" - " Example: --dump:raw_gc_map\n" - " Default: neither\n" + " --dump:raw_mapping_table enables dumping of the mapping table.\n" + " Example: --dump:raw_mapping_table\n" + "\n"); + fprintf(stderr, + " --dump:raw_mapping_table enables dumping of the GC map.\n" + " Example: --dump:raw_gc_map\n" + "\n"); + fprintf(stderr, + " --no-dump:vmap may be used to disable vmap dumping.\n" + " Example: --no-dump:vmap\n" + "\n"); + fprintf(stderr, + " --no-disassemble may be used to disable disassembly.\n" + " Example: --no-disassemble\n" "\n"); exit(EXIT_FAILURE); } @@ -326,18 +337,45 @@ class OatSymbolizer : public CodeOutput { std::string output_name_; }; +class OatDumperOptions { + public: + OatDumperOptions(bool dump_raw_mapping_table, + bool dump_raw_gc_map, + bool dump_vmap, + bool disassemble_code, + bool absolute_addresses) + : dump_raw_mapping_table_(dump_raw_mapping_table), + dump_raw_gc_map_(dump_raw_gc_map), + dump_vmap_(dump_vmap), + disassemble_code_(disassemble_code), + absolute_addresses_(absolute_addresses) {} + + const bool dump_raw_mapping_table_; + const bool dump_raw_gc_map_; + const bool dump_vmap_; + const bool disassemble_code_; + const bool absolute_addresses_; +}; + class OatDumper { public: - explicit OatDumper(const OatFile& oat_file, bool dump_raw_mapping_table, bool dump_raw_gc_map) + explicit OatDumper(const OatFile& oat_file, OatDumperOptions* options) : oat_file_(oat_file), oat_dex_files_(oat_file.GetOatDexFiles()), - dump_raw_mapping_table_(dump_raw_mapping_table), - dump_raw_gc_map_(dump_raw_gc_map), - disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet())) { + options_(options), + disassembler_(Disassembler::Create(oat_file_.GetOatHeader().GetInstructionSet(), + new DisassemblerOptions(options_->absolute_addresses_, + oat_file.Begin()))) { AddAllOffsets(); } - void Dump(std::ostream& os) { + ~OatDumper() { + delete options_; + delete disassembler_; + } + + bool Dump(std::ostream& os) { + bool success = true; const OatHeader& oat_header = oat_file_.GetOatHeader(); os << "MAGIC:\n"; @@ -358,7 +396,7 @@ class OatDumper { #define DUMP_OAT_HEADER_OFFSET(label, offset) \ os << label " OFFSET:\n"; \ os << StringPrintf("0x%08x", oat_header.offset()); \ - if (oat_header.offset() != 0) { \ + if (oat_header.offset() != 0 && options_->absolute_addresses_) { \ os << StringPrintf(" (%p)", oat_file_.Begin() + oat_header.offset()); \ } \ os << StringPrintf("\n\n"); @@ -386,7 +424,10 @@ class OatDumper { GetQuickToInterpreterBridgeOffset); #undef DUMP_OAT_HEADER_OFFSET - os << "IMAGE PATCH DELTA:\n" << oat_header.GetImagePatchDelta() << "\n\n"; + os << "IMAGE PATCH DELTA:\n"; + os << StringPrintf("%d (0x%08x)\n\n", + oat_header.GetImagePatchDelta(), + oat_header.GetImagePatchDelta()); os << "IMAGE FILE LOCATION OAT CHECKSUM:\n"; os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum()); @@ -407,19 +448,28 @@ class OatDumper { os << "\n"; } - os << "BEGIN:\n"; - os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n"; + if (options_->absolute_addresses_) { + os << "BEGIN:\n"; + os << reinterpret_cast<const void*>(oat_file_.Begin()) << "\n\n"; + + os << "END:\n"; + os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n"; + } - os << "END:\n"; - os << reinterpret_cast<const void*>(oat_file_.End()) << "\n\n"; + os << "SIZE:\n"; + os << oat_file_.Size() << "\n\n"; os << std::flush; for (size_t i = 0; i < oat_dex_files_.size(); i++) { const OatFile::OatDexFile* oat_dex_file = oat_dex_files_[i]; CHECK(oat_dex_file != nullptr); - DumpOatDexFile(os, *oat_dex_file); + if (!DumpOatDexFile(os, *oat_dex_file)) { + success = false; + } } + os << std::flush; + return success; } size_t ComputeSize(const void* oat_data) { @@ -507,6 +557,10 @@ class OatDumper { offsets_.insert(oat_file_.Size()); } + static uint32_t AlignCodeOffset(uint32_t maybe_thumb_offset) { + return maybe_thumb_offset & ~0x1; // TODO: Make this Thumb2 specific. + } + void AddOffsets(const OatFile::OatMethod& oat_method) { uint32_t code_offset = oat_method.GetCodeOffset(); if (oat_file_.GetOatHeader().GetInstructionSet() == kThumb2) { @@ -518,8 +572,9 @@ class OatDumper { offsets_.insert(oat_method.GetNativeGcMapOffset()); } - void DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { - os << "OAT DEX FILE:\n"; + bool DumpOatDexFile(std::ostream& os, const OatFile::OatDexFile& oat_dex_file) { + bool success = true; + os << "OatDexFile:\n"; os << StringPrintf("location: %s\n", oat_dex_file.GetDexFileLocation().c_str()); os << StringPrintf("checksum: 0x%08x\n", oat_dex_file.GetDexFileLocationChecksum()); @@ -529,24 +584,30 @@ class OatDumper { std::unique_ptr<const DexFile> dex_file(oat_dex_file.OpenDexFile(&error_msg)); if (dex_file.get() == nullptr) { os << "NOT FOUND: " << error_msg << "\n\n"; - return; + os << std::flush; + return false; } for (size_t class_def_index = 0; class_def_index < dex_file->NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const char* descriptor = dex_file->GetClassDescriptor(class_def); + uint32_t oat_class_offset = oat_dex_file.GetOatClassOffset(class_def_index); const OatFile::OatClass oat_class = oat_dex_file.GetOatClass(class_def_index); - os << StringPrintf("%zd: %s (type_idx=%d)", class_def_index, descriptor, class_def.class_idx_) + os << StringPrintf("%zd: %s (offset=0x%08x) (type_idx=%d)", + class_def_index, descriptor, oat_class_offset, class_def.class_idx_) << " (" << oat_class.GetStatus() << ")" << " (" << oat_class.GetType() << ")\n"; // TODO: include bitmap here if type is kOatClassSomeCompiled? Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indented_os(&indent_filter); - DumpOatClass(indented_os, oat_class, *(dex_file.get()), class_def); + if (!DumpOatClass(indented_os, oat_class, *(dex_file.get()), class_def)) { + success = false; + } } os << std::flush; + return success; } static void SkipAllFields(ClassDataItemIterator& it) { @@ -558,38 +619,51 @@ class OatDumper { } } - void DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file, + bool DumpOatClass(std::ostream& os, const OatFile::OatClass& oat_class, const DexFile& dex_file, const DexFile::ClassDef& class_def) { + bool success = true; const byte* class_data = dex_file.GetClassData(class_def); if (class_data == nullptr) { // empty class such as a marker interface? - return; + os << std::flush; + return success; } ClassDataItemIterator it(dex_file, class_data); SkipAllFields(it); - uint32_t class_method_idx = 0; + uint32_t class_method_index = 0; while (it.HasNextDirectMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file, - it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetRawMemberAccessFlags()); - class_method_idx++; + if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file, + it.GetMemberIndex(), it.GetMethodCodeItem(), + it.GetRawMemberAccessFlags())) { + success = false; + } + class_method_index++; it.Next(); } while (it.HasNextVirtualMethod()) { - const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx); - DumpOatMethod(os, class_def, class_method_idx, oat_method, dex_file, - it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetRawMemberAccessFlags()); - class_method_idx++; + if (!DumpOatMethod(os, class_def, class_method_index, oat_class, dex_file, + it.GetMemberIndex(), it.GetMethodCodeItem(), + it.GetRawMemberAccessFlags())) { + success = false; + } + class_method_index++; it.Next(); } DCHECK(!it.HasNext()); os << std::flush; + return success; } - void DumpOatMethod(std::ostream& os, const DexFile::ClassDef& class_def, + static constexpr uint32_t kPrologueBytes = 16; + + // When this was picked, the largest arm method was 55,256 bytes and arm64 was 50,412 bytes. + static constexpr uint32_t kMaxCodeSize = 100 * 1000; + + bool DumpOatMethod(std::ostream& os, const DexFile::ClassDef& class_def, uint32_t class_method_index, - const OatFile::OatMethod& oat_method, const DexFile& dex_file, + const OatFile::OatClass& oat_class, const DexFile& dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code_item, uint32_t method_access_flags) { + bool success = true; os << StringPrintf("%d: %s (dex_method_idx=%d)\n", class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(), dex_method_idx); @@ -608,52 +682,192 @@ class OatDumper { verifier.reset(DumpVerifier(*indent2_os, dex_method_idx, &dex_file, class_def, code_item, method_access_flags)); } + + uint32_t oat_method_offsets_offset = oat_class.GetOatMethodOffsetsOffset(class_method_index); + const OatMethodOffsets* oat_method_offsets = oat_class.GetOatMethodOffsets(class_method_index); + const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index); { - *indent1_os << "OAT DATA:\n"; + *indent1_os << "OatMethodOffsets "; + if (options_->absolute_addresses_) { + *indent1_os << StringPrintf("%p ", oat_method_offsets); + } + *indent1_os << StringPrintf("(offset=0x%08x)\n", oat_method_offsets_offset); + if (oat_method_offsets_offset > oat_file_.Size()) { + *indent1_os << StringPrintf( + "WARNING: oat method offsets offset 0x%08x is past end of file 0x%08zx.\n", + oat_method_offsets_offset, oat_file_.Size()); + // If we can't read OatMethodOffsets, the rest of the data is dangerous to read. + os << std::flush; + return false; + } - *indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes()); - *indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask()); - DumpSpillMask(*indent2_os, oat_method.GetCoreSpillMask(), false); - *indent2_os << StringPrintf("\nfp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask()); - DumpSpillMask(*indent2_os, oat_method.GetFpSpillMask(), true); - *indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n", - oat_method.GetVmapTable(), oat_method.GetVmapTableOffset()); + uint32_t code_offset = oat_method.GetCodeOffset(); + *indent2_os << StringPrintf("code_offset: 0x%08x ", code_offset); + uint32_t aligned_code_begin = AlignCodeOffset(oat_method.GetCodeOffset()); + if (aligned_code_begin > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "code offset 0x%08x is past end of file 0x%08zx.\n", + aligned_code_begin, oat_file_.Size()); + success = false; + } + *indent2_os << "\n"; - if (oat_method.GetNativeGcMap() != nullptr) { - // The native GC map is null for methods compiled with the optimizing compiler. - DumpVmap(*indent2_os, oat_method); + *indent2_os << "gc_map: "; + if (options_->absolute_addresses_) { + *indent2_os << StringPrintf("%p ", oat_method.GetNativeGcMap()); } - DumpVregLocations(*indent2_os, oat_method, code_item); - *indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n", - oat_method.GetMappingTable(), oat_method.GetMappingTableOffset()); - if (dump_raw_mapping_table_) { + uint32_t gc_map_offset = oat_method.GetNativeGcMapOffset(); + *indent2_os << StringPrintf("(offset=0x%08x)\n", gc_map_offset); + if (gc_map_offset > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "gc map table offset 0x%08x is past end of file 0x%08zx.\n", + gc_map_offset, oat_file_.Size()); + success = false; + } else if (options_->dump_raw_gc_map_) { Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent3_os(&indent3_filter); - DumpMappingTable(indent3_os, oat_method); + DumpGcMap(indent3_os, oat_method, code_item); + } + } + { + *indent1_os << "OatQuickMethodHeader "; + uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset(); + const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader(); + + if (options_->absolute_addresses_) { + *indent1_os << StringPrintf("%p ", method_header); } - *indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n", - oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset()); - if (dump_raw_gc_map_) { + *indent1_os << StringPrintf("(offset=0x%08x)\n", method_header_offset); + if (method_header_offset > oat_file_.Size()) { + *indent1_os << StringPrintf( + "WARNING: oat quick method header offset 0x%08x is past end of file 0x%08zx.\n", + method_header_offset, oat_file_.Size()); + // If we can't read the OatQuickMethodHeader, the rest of the data is dangerous to read. + os << std::flush; + return false; + } + + *indent2_os << "mapping_table: "; + if (options_->absolute_addresses_) { + *indent2_os << StringPrintf("%p ", oat_method.GetMappingTable()); + } + uint32_t mapping_table_offset = oat_method.GetMappingTableOffset(); + *indent2_os << StringPrintf("(offset=0x%08x)\n", oat_method.GetMappingTableOffset()); + if (mapping_table_offset > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "mapping table offset 0x%08x is past end of file 0x%08zx. " + "mapping table offset was loaded from offset 0x%08x.\n", + mapping_table_offset, oat_file_.Size(), + oat_method.GetMappingTableOffsetOffset()); + success = false; + } else if (options_->dump_raw_mapping_table_) { Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count); std::ostream indent3_os(&indent3_filter); - DumpGcMap(indent3_os, oat_method, code_item); + DumpMappingTable(indent3_os, oat_method); + } + + *indent2_os << "vmap_table: "; + if (options_->absolute_addresses_) { + *indent2_os << StringPrintf("%p ", oat_method.GetVmapTable()); + } + uint32_t vmap_table_offset = oat_method.GetVmapTableOffset(); + *indent2_os << StringPrintf("(offset=0x%08x)\n", vmap_table_offset); + if (vmap_table_offset > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "vmap table offset 0x%08x is past end of file 0x%08zx. " + "vmap table offset was loaded from offset 0x%08x.\n", + vmap_table_offset, oat_file_.Size(), + oat_method.GetVmapTableOffsetOffset()); + success = false; + } else if (options_->dump_vmap_) { + if (oat_method.GetNativeGcMap() != nullptr) { + // The native GC map is null for methods compiled with the optimizing compiler. + DumpVmap(*indent2_os, oat_method); + } } } { - const void* code = oat_method.GetQuickCode(); - uint32_t code_size = oat_method.GetQuickCodeSize(); - if (code == nullptr) { - code = oat_method.GetPortableCode(); - code_size = oat_method.GetPortableCodeSize(); - } - *indent1_os << StringPrintf("CODE: %p (offset=0x%08x size=%d)%s\n", - code, - oat_method.GetCodeOffset(), - code_size, - code != nullptr ? "..." : ""); + *indent1_os << "QuickMethodFrameInfo\n"; + + *indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes()); + *indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask()); + DumpSpillMask(*indent2_os, oat_method.GetCoreSpillMask(), false); + *indent2_os << "\n"; + *indent2_os << StringPrintf("fp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask()); + DumpSpillMask(*indent2_os, oat_method.GetFpSpillMask(), true); + *indent2_os << "\n"; + } + { + // Based on spill masks from QuickMethodFrameInfo so placed + // after it is dumped, but useful for understanding quick + // code, so dumped here. + DumpVregLocations(*indent2_os, oat_method, code_item); + } + { + *indent1_os << "CODE: "; + uint32_t code_size_offset = oat_method.GetQuickCodeSizeOffset(); + if (code_size_offset > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "code size offset 0x%08x is past end of file 0x%08zx.", + code_size_offset, oat_file_.Size()); + success = false; + } else { + const void* code = oat_method.GetQuickCode(); + uint32_t code_size = oat_method.GetQuickCodeSize(); + if (code == nullptr) { + code = oat_method.GetPortableCode(); + code_size = oat_method.GetPortableCodeSize(); + code_size_offset = 0; + } + uint32_t code_offset = oat_method.GetCodeOffset(); + uint32_t aligned_code_begin = AlignCodeOffset(code_offset); + uint64_t aligned_code_end = aligned_code_begin + code_size; - DumpCode(*indent2_os, verifier.get(), oat_method, code_item); + if (options_->absolute_addresses_) { + *indent1_os << StringPrintf("%p ", code); + } + *indent1_os << StringPrintf("(code_offset=0x%08x size_offset=0x%08x size=%u)%s\n", + code_offset, + code_size_offset, + code_size, + code != nullptr ? "..." : ""); + + if (aligned_code_begin > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "start of code at 0x%08x is past end of file 0x%08zx.", + aligned_code_begin, oat_file_.Size()); + success = false; + } else if (aligned_code_end > oat_file_.Size()) { + *indent2_os << StringPrintf("WARNING: " + "end of code at 0x%08" PRIx64 " is past end of file 0x%08zx. " + "code size is 0x%08x loaded from offset 0x%08x.\n", + aligned_code_end, oat_file_.Size(), + code_size, code_size_offset); + success = false; + if (options_->disassemble_code_) { + if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { + DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes); + } + } + } else if (code_size > kMaxCodeSize) { + *indent2_os << StringPrintf("WARNING: " + "code size %d is bigger than max expected threshold of %d. " + "code size is 0x%08x loaded from offset 0x%08x.\n", + code_size, kMaxCodeSize, + code_size, code_size_offset); + success = false; + if (options_->disassemble_code_) { + if (code_size_offset + kPrologueBytes <= oat_file_.Size()) { + DumpCode(*indent2_os, verifier.get(), oat_method, code_item, true, kPrologueBytes); + } + } + } else if (options_->disassemble_code_) { + DumpCode(*indent2_os, verifier.get(), oat_method, code_item, !success, 0); + } + } } + os << std::flush; + return success; } void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) { @@ -968,11 +1182,14 @@ class OatDumper { } void DumpCode(std::ostream& os, verifier::MethodVerifier* verifier, - const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item) { + const OatFile::OatMethod& oat_method, const DexFile::CodeItem* code_item, + bool bad_input, size_t code_size) { const void* portable_code = oat_method.GetPortableCode(); const void* quick_code = oat_method.GetQuickCode(); - size_t code_size = oat_method.GetQuickCodeSize(); + if (code_size == 0) { + code_size = oat_method.GetQuickCodeSize(); + } if ((code_size == 0) || ((portable_code == nullptr) && (quick_code == nullptr))) { os << "NO CODE!\n"; return; @@ -980,13 +1197,17 @@ class OatDumper { const uint8_t* quick_native_pc = reinterpret_cast<const uint8_t*>(quick_code); size_t offset = 0; while (offset < code_size) { - DumpMappingAtOffset(os, oat_method, offset, false); + if (!bad_input) { + DumpMappingAtOffset(os, oat_method, offset, false); + } offset += disassembler_->Dump(os, quick_native_pc + offset); - uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true); - if (dex_pc != DexFile::kDexNoIndex) { - DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset); - if (verifier != nullptr) { - DumpVRegsAtDexPc(os, verifier, oat_method, code_item, dex_pc); + if (!bad_input) { + uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true); + if (dex_pc != DexFile::kDexNoIndex) { + DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset); + if (verifier != nullptr) { + DumpVRegsAtDexPc(os, verifier, oat_method, code_item, dex_pc); + } } } } @@ -997,23 +1218,22 @@ class OatDumper { } const OatFile& oat_file_; - std::vector<const OatFile::OatDexFile*> oat_dex_files_; - bool dump_raw_mapping_table_; - bool dump_raw_gc_map_; + const std::vector<const OatFile::OatDexFile*> oat_dex_files_; + const OatDumperOptions* options_; std::set<uintptr_t> offsets_; - std::unique_ptr<Disassembler> disassembler_; + Disassembler* disassembler_; }; class ImageDumper { public: explicit ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space, - const ImageHeader& image_header, bool dump_raw_mapping_table, - bool dump_raw_gc_map) - : os_(os), image_space_(image_space), image_header_(image_header), - dump_raw_mapping_table_(dump_raw_mapping_table), - dump_raw_gc_map_(dump_raw_gc_map) {} + const ImageHeader& image_header, OatDumperOptions* oat_dumper_options) + : os_(os), + image_space_(image_space), + image_header_(image_header), + oat_dumper_options_(oat_dumper_options) {} - void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + bool Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::ostream& os = *os_; os << "MAGIC: " << image_header_.GetMagic() << "\n\n"; @@ -1087,15 +1307,14 @@ class ImageDumper { oat_file = OatFile::Open(oat_location, oat_location, nullptr, false, &error_msg); if (oat_file == nullptr) { os << "NOT FOUND: " << error_msg << "\n"; - return; + return false; } } os << "\n"; stats_.oat_file_bytes = oat_file->Size(); - oat_dumper_.reset(new OatDumper(*oat_file, dump_raw_mapping_table_, - dump_raw_gc_map_)); + oat_dumper_.reset(new OatDumper(*oat_file, oat_dumper_options_.release())); for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { CHECK(oat_dex_file != nullptr); @@ -1163,7 +1382,7 @@ class ImageDumper { os << std::flush; - oat_dumper_->Dump(os); + return oat_dumper_->Dump(os); } private: @@ -1727,12 +1946,11 @@ class ImageDumper { // threshold, we assume 2 bytes per instruction and 2 instructions per block. kLargeMethodDexBytes = 16000 }; - std::unique_ptr<OatDumper> oat_dumper_; std::ostream* os_; gc::space::ImageSpace& image_space_; const ImageHeader& image_header_; - bool dump_raw_mapping_table_; - bool dump_raw_gc_map_; + std::unique_ptr<OatDumper> oat_dumper_; + std::unique_ptr<OatDumperOptions> oat_dumper_options_; DISALLOW_COPY_AND_ASSIGN(ImageDumper); }; @@ -1759,6 +1977,8 @@ static int oatdump(int argc, char** argv) { std::string output_name; bool dump_raw_mapping_table = false; bool dump_raw_gc_map = false; + bool dump_vmap = true; + bool disassemble_code = true; bool symbolize = false; for (int i = 0; i < argc; i++) { @@ -1782,15 +2002,14 @@ static int oatdump(int argc, char** argv) { } else if (instruction_set_str == "x86_64") { instruction_set = kX86_64; } - } else if (option.starts_with("--dump:")) { - if (option == "--dump:raw_mapping_table") { - dump_raw_mapping_table = true; - } else if (option == "--dump:raw_gc_map") { - dump_raw_gc_map = true; - } else { - fprintf(stderr, "Unknown argument %s\n", option.data()); - usage(); - } + } else if (option =="--dump:raw_mapping_table") { + dump_raw_mapping_table = true; + } else if (option == "--dump:raw_gc_map") { + dump_raw_gc_map = true; + } else if (option == "--no-dump:vmap") { + dump_vmap = false; + } else if (option == "--no-disassemble") { + disassemble_code = false; } else if (option.starts_with("--output=")) { output_name = option.substr(strlen("--output=")).ToString(); const char* filename = output_name.c_str(); @@ -1819,6 +2038,13 @@ static int oatdump(int argc, char** argv) { return EXIT_FAILURE; } + // If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping. + bool absolute_addresses = (oat_filename == nullptr); + std::unique_ptr<OatDumperOptions> oat_dumper_options(new OatDumperOptions(dump_raw_mapping_table, + dump_raw_gc_map, + dump_vmap, + disassemble_code, + absolute_addresses)); if (oat_filename != nullptr) { std::string error_msg; OatFile* oat_file = @@ -1838,8 +2064,9 @@ static int oatdump(int argc, char** argv) { return EXIT_FAILURE; } } else { - OatDumper oat_dumper(*oat_file, dump_raw_mapping_table, dump_raw_gc_map); - oat_dumper.Dump(*os); + OatDumper oat_dumper(*oat_file, oat_dumper_options.release()); + bool success = oat_dumper.Dump(*os); + return (success) ? EXIT_SUCCESS : EXIT_FAILURE; } return EXIT_SUCCESS; } @@ -1885,10 +2112,9 @@ static int oatdump(int argc, char** argv) { fprintf(stderr, "Invalid image header %s\n", image_location); return EXIT_FAILURE; } - ImageDumper image_dumper(os, *image_space, image_header, - dump_raw_mapping_table, dump_raw_gc_map); - image_dumper.Dump(); - return EXIT_SUCCESS; + ImageDumper image_dumper(os, *image_space, image_header, oat_dumper_options.release()); + bool success = image_dumper.Dump(); + return (success) ? EXIT_SUCCESS : EXIT_FAILURE; } } // namespace art diff --git a/runtime/base/allocator.h b/runtime/base/allocator.h index a7adb02e29..2c3e966d32 100644 --- a/runtime/base/allocator.h +++ b/runtime/base/allocator.h @@ -66,6 +66,7 @@ enum AllocatorTag { kAllocatorTagCompileTimeClassPath, kAllocatorTagOatFile, kAllocatorTagDexFileVerifier, + kAllocatorTagRosAlloc, kAllocatorTagCount, // Must always be last element. }; std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag); @@ -149,6 +150,10 @@ class AllocationTrackingMultiMap : public std::multimap< Key, T, Compare, TrackingAllocator<std::pair<Key, T>, kTag>> { }; +template<class Key, AllocatorTag kTag, class Compare = std::less<Key>> +class AllocationTrackingSet : public std::set<Key, Compare, TrackingAllocator<Key, kTag>> { +}; + } // namespace art #endif // ART_RUNTIME_BASE_ALLOCATOR_H_ diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 455680b449..2c95eded08 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -884,6 +884,10 @@ void Locks::Init() { DCHECK(heap_bitmap_lock_ == nullptr); heap_bitmap_lock_ = new ReaderWriterMutex("heap bitmap lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kTraceLock); + DCHECK(trace_lock_ == nullptr); + trace_lock_ = new Mutex("trace lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kRuntimeShutdownLock); DCHECK(runtime_shutdown_lock_ == nullptr); runtime_shutdown_lock_ = new Mutex("runtime shutdown lock", current_lock_level); @@ -892,10 +896,6 @@ void Locks::Init() { DCHECK(profiler_lock_ == nullptr); profiler_lock_ = new Mutex("profiler lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kTraceLock); - DCHECK(trace_lock_ == nullptr); - trace_lock_ = new Mutex("trace lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kDeoptimizationLock); DCHECK(deoptimization_lock_ == nullptr); deoptimization_lock_ = new Mutex("Deoptimization lock", current_lock_level); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 20f58de888..8d2cdce802 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -92,12 +92,12 @@ enum LockLevel { kBreakpointInvokeLock, kAllocTrackerLock, kDeoptimizationLock, - kTraceLock, kProfilerLock, kJdwpEventListLock, kJdwpAttachLock, kJdwpStartLock, kRuntimeShutdownLock, + kTraceLock, kHeapBitmapLock, kMutatorLock, kThreadListSuspendThreadLock, diff --git a/runtime/base/stringprintf_test.cc b/runtime/base/stringprintf_test.cc new file mode 100644 index 0000000000..0bfde33a3f --- /dev/null +++ b/runtime/base/stringprintf_test.cc @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stringprintf.h" + +#include "gtest/gtest.h" + +namespace art { + +TEST(StringPrintfTest, HexSizeT) { + size_t size = 0x00107e59; + EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str()); + EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str()); +} + +} // namespace art diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h new file mode 100644 index 0000000000..1a78d72657 --- /dev/null +++ b/runtime/check_reference_map_visitor.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ +#define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ + +#include "gc_map.h" +#include "mirror/art_method-inl.h" +#include "scoped_thread_state_change.h" +#include "stack_map.h" + +namespace art { + +// Helper class for tests checking that the compiler keeps track of dex registers +// holding references. +class CheckReferenceMapVisitor : public StackVisitor { + public: + explicit CheckReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : StackVisitor(thread, nullptr) {} + + bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + if (m->IsCalleeSaveMethod() || m->IsNative()) { + CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex); + } + + if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) { + return true; + } + + LOG(INFO) << "At " << PrettyMethod(m, false); + + if (m->IsCalleeSaveMethod()) { + LOG(WARNING) << "no PC for " << PrettyMethod(m); + return true; + } + + return false; + } + + void CheckReferences(int* registers, int number_of_references, uint32_t native_pc_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + if (GetMethod()->IsOptimized()) { + CheckOptimizedMethod(registers, number_of_references, native_pc_offset); + } else { + CheckQuickMethod(registers, number_of_references, native_pc_offset); + } + } + + private: + void CheckOptimizedMethod(int* registers, int number_of_references, uint32_t native_pc_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + CodeInfo code_info = m->GetOptimizedCodeInfo(); + StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset); + DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map, m->GetCodeItem()->registers_size_); + MemoryRegion stack_mask = stack_map.GetStackMask(); + uint32_t register_mask = stack_map.GetRegisterMask(); + for (int i = 0; i < number_of_references; ++i) { + int reg = registers[i]; + CHECK(reg < m->GetCodeItem()->registers_size_); + DexRegisterMap::LocationKind location = dex_register_map.GetLocationKind(reg); + switch (location) { + case DexRegisterMap::kNone: + // Not set, should not be a reference. + CHECK(false); + break; + case DexRegisterMap::kInStack: + CHECK(stack_mask.LoadBit(dex_register_map.GetValue(reg) >> 2)); + break; + case DexRegisterMap::kInRegister: + CHECK_NE(register_mask & dex_register_map.GetValue(reg), 0u); + break; + case DexRegisterMap::kConstant: + CHECK_EQ(dex_register_map.GetValue(0), 0); + break; + } + } + } + + void CheckQuickMethod(int* registers, int number_of_references, uint32_t native_pc_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* m = GetMethod(); + NativePcOffsetToReferenceMap map(m->GetNativeGcMap()); + const uint8_t* ref_bitmap = map.FindBitMap(native_pc_offset); + CHECK(ref_bitmap); + for (int i = 0; i < number_of_references; ++i) { + int reg = registers[i]; + CHECK(reg < m->GetCodeItem()->registers_size_); + CHECK((*((ref_bitmap) + reg / 8) >> (reg % 8) ) & 0x01) + << "Error: Reg @" << i << " is not in GC map"; + } + } +}; + +} // namespace art + +#endif // ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_ diff --git a/runtime/debugger.cc b/runtime/debugger.cc index cc1e0b994a..6c374029d8 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1886,11 +1886,25 @@ JDWP::JdwpError Dbg::SetStaticFieldValue(JDWP::FieldId field_id, uint64_t value, return SetFieldValueImpl(0, field_id, value, width, true); } -std::string Dbg::StringToUtf8(JDWP::ObjectId string_id) { +JDWP::JdwpError Dbg::StringToUtf8(JDWP::ObjectId string_id, std::string* str) { JDWP::JdwpError error; - mirror::String* s = gRegistry->Get<mirror::String*>(string_id, &error); - CHECK(s != nullptr) << error; - return s->ToModifiedUtf8(); + mirror::Object* obj = gRegistry->Get<mirror::Object*>(string_id, &error); + if (error != JDWP::ERR_NONE) { + return error; + } + if (obj == nullptr) { + return JDWP::ERR_INVALID_OBJECT; + } + { + ScopedObjectAccessUnchecked soa(Thread::Current()); + mirror::Class* java_lang_String = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_String); + if (!java_lang_String->IsAssignableFrom(obj->GetClass())) { + // This isn't a string. + return JDWP::ERR_INVALID_STRING; + } + } + *str = obj->AsString()->ToModifiedUtf8(); + return JDWP::ERR_NONE; } void Dbg::OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply) { @@ -1939,7 +1953,7 @@ JDWP::JdwpError Dbg::GetThreadName(JDWP::ObjectId thread_id, std::string* name) } JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) { - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccessUnchecked soa(Thread::Current()); JDWP::JdwpError error; mirror::Object* thread_object = gRegistry->Get<mirror::Object*>(thread_id, &error); if (error != JDWP::ERR_NONE) { @@ -1970,26 +1984,54 @@ JDWP::JdwpError Dbg::GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* p return error; } -std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) { - ScopedObjectAccess soa(Thread::Current()); +static mirror::Object* DecodeThreadGroup(ScopedObjectAccessUnchecked& soa, + JDWP::ObjectId thread_group_id, JDWP::JdwpError* error) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, error); + if (*error != JDWP::ERR_NONE) { + return nullptr; + } + if (thread_group == nullptr) { + *error = JDWP::ERR_INVALID_OBJECT; + return nullptr; + } + mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup); + CHECK(c != nullptr); + if (!c->IsAssignableFrom(thread_group->GetClass())) { + // This is not a java.lang.ThreadGroup. + *error = JDWP::ERR_INVALID_THREAD_GROUP; + return nullptr; + } + *error = JDWP::ERR_NONE; + return thread_group; +} + +JDWP::JdwpError Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) { + ScopedObjectAccessUnchecked soa(Thread::Current()); JDWP::JdwpError error; - mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, &error); - CHECK(thread_group != nullptr) << error; + mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error); + if (error != JDWP::ERR_NONE) { + return error; + } const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName"); mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup); - CHECK(c != nullptr); mirror::ArtField* f = c->FindInstanceField("name", "Ljava/lang/String;"); CHECK(f != nullptr); mirror::String* s = reinterpret_cast<mirror::String*>(f->GetObject(thread_group)); soa.Self()->EndAssertNoThreadSuspension(old_cause); - return s->ToModifiedUtf8(); + + std::string thread_group_name(s->ToModifiedUtf8()); + expandBufAddUtf8String(pReply, thread_group_name); + return JDWP::ERR_NONE; } -JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) { +JDWP::JdwpError Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id, JDWP::ExpandBuf* pReply) { ScopedObjectAccessUnchecked soa(Thread::Current()); JDWP::JdwpError error; - mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, &error); - CHECK(thread_group != nullptr) << error; + mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error); + if (error != JDWP::ERR_NONE) { + return error; + } const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupParent"); mirror::Class* c = soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_ThreadGroup); CHECK(c != nullptr); @@ -1997,19 +2039,69 @@ JDWP::ObjectId Dbg::GetThreadGroupParent(JDWP::ObjectId thread_group_id) { CHECK(f != nullptr); mirror::Object* parent = f->GetObject(thread_group); soa.Self()->EndAssertNoThreadSuspension(old_cause); - return gRegistry->Add(parent); + + JDWP::ObjectId parent_group_id = gRegistry->Add(parent); + expandBufAddObjectId(pReply, parent_group_id); + return JDWP::ERR_NONE; } -JDWP::ObjectId Dbg::GetSystemThreadGroupId() { +static void GetChildThreadGroups(ScopedObjectAccessUnchecked& soa, mirror::Object* thread_group, + std::vector<JDWP::ObjectId>* child_thread_group_ids) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(thread_group != nullptr); + + // Get the ArrayList<ThreadGroup> "groups" out of this thread group... + mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;"); + mirror::Object* groups_array_list = groups_field->GetObject(thread_group); + + // Get the array and size out of the ArrayList<ThreadGroup>... + mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;"); + mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I"); + mirror::ObjectArray<mirror::Object>* groups_array = + array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>(); + const int32_t size = size_field->GetInt(groups_array_list); + + // Copy the first 'size' elements out of the array into the result. + for (int32_t i = 0; i < size; ++i) { + child_thread_group_ids->push_back(gRegistry->Add(groups_array->Get(i))); + } +} + +JDWP::JdwpError Dbg::GetThreadGroupChildren(JDWP::ObjectId thread_group_id, + JDWP::ExpandBuf* pReply) { ScopedObjectAccessUnchecked soa(Thread::Current()); - mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup); - mirror::Object* group = f->GetObject(f->GetDeclaringClass()); - return gRegistry->Add(group); + JDWP::JdwpError error; + mirror::Object* thread_group = DecodeThreadGroup(soa, thread_group_id, &error); + if (error != JDWP::ERR_NONE) { + return error; + } + + // Add child threads. + { + std::vector<JDWP::ObjectId> child_thread_ids; + GetThreads(thread_group, &child_thread_ids); + expandBufAdd4BE(pReply, child_thread_ids.size()); + for (JDWP::ObjectId child_thread_id : child_thread_ids) { + expandBufAddObjectId(pReply, child_thread_id); + } + } + + // Add child thread groups. + { + std::vector<JDWP::ObjectId> child_thread_groups_ids; + GetChildThreadGroups(soa, thread_group, &child_thread_groups_ids); + expandBufAdd4BE(pReply, child_thread_groups_ids.size()); + for (JDWP::ObjectId child_thread_group_id : child_thread_groups_ids) { + expandBufAddObjectId(pReply, child_thread_group_id); + } + } + + return JDWP::ERR_NONE; } -JDWP::ObjectId Dbg::GetMainThreadGroupId() { - ScopedObjectAccess soa(Thread::Current()); - mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup); +JDWP::ObjectId Dbg::GetSystemThreadGroupId() { + ScopedObjectAccessUnchecked soa(Thread::Current()); + mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup); mirror::Object* group = f->GetObject(f->GetDeclaringClass()); return gRegistry->Add(group); } @@ -2111,11 +2203,8 @@ static bool IsInDesiredThreadGroup(ScopedObjectAccessUnchecked& soa, return (group == desired_thread_group); } -void Dbg::GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>* thread_ids) { +void Dbg::GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids) { ScopedObjectAccessUnchecked soa(Thread::Current()); - JDWP::JdwpError error; - mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, &error); - CHECK_EQ(error, JDWP::ERR_NONE); std::list<Thread*> all_threads_list; { MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); @@ -2147,30 +2236,6 @@ void Dbg::GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId> } } -void Dbg::GetChildThreadGroups(JDWP::ObjectId thread_group_id, - std::vector<JDWP::ObjectId>* child_thread_group_ids) { - ScopedObjectAccess soa(Thread::Current()); - JDWP::JdwpError error; - mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, &error); - CHECK(thread_group != nullptr) << error; - - // Get the ArrayList<ThreadGroup> "groups" out of this thread group... - mirror::ArtField* groups_field = thread_group->GetClass()->FindInstanceField("groups", "Ljava/util/List;"); - mirror::Object* groups_array_list = groups_field->GetObject(thread_group); - - // Get the array and size out of the ArrayList<ThreadGroup>... - mirror::ArtField* array_field = groups_array_list->GetClass()->FindInstanceField("array", "[Ljava/lang/Object;"); - mirror::ArtField* size_field = groups_array_list->GetClass()->FindInstanceField("size", "I"); - mirror::ObjectArray<mirror::Object>* groups_array = - array_field->GetObject(groups_array_list)->AsObjectArray<mirror::Object>(); - const int32_t size = size_field->GetInt(groups_array_list); - - // Copy the first 'size' elements out of the array into the result. - for (int32_t i = 0; i < size; ++i) { - child_thread_group_ids->push_back(gRegistry->Add(groups_array->Get(i))); - } -} - static int GetStackDepth(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { struct CountStackDepthVisitor : public StackVisitor { explicit CountStackDepthVisitor(Thread* thread) @@ -4323,7 +4388,7 @@ void Dbg::SetAllocTrackingEnabled(bool enable) { recent_allocation_records_ = new AllocRecord[alloc_record_max_]; CHECK(recent_allocation_records_ != nullptr); } - Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(false); } else { { ScopedObjectAccess soa(self); // For type_cache_.Clear(); @@ -4339,7 +4404,7 @@ void Dbg::SetAllocTrackingEnabled(bool enable) { type_cache_.Clear(); } // If an allocation comes in before we uninstrument, we will safely drop it on the floor. - Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints(false); } } diff --git a/runtime/debugger.h b/runtime/debugger.h index 219210e4aa..e171d7854f 100644 --- a/runtime/debugger.h +++ b/runtime/debugger.h @@ -381,7 +381,7 @@ class Dbg { static JDWP::JdwpError SetStaticFieldValue(JDWP::FieldId field_id, uint64_t value, int width) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static std::string StringToUtf8(JDWP::ObjectId string_id) + static JDWP::JdwpError StringToUtf8(JDWP::ObjectId string_id, std::string* str) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static void OutputJValue(JDWP::JdwpTag tag, const JValue* return_value, JDWP::ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -393,13 +393,19 @@ class Dbg { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_); static JDWP::JdwpError GetThreadGroup(JDWP::ObjectId thread_id, JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) LOCKS_EXCLUDED(Locks::thread_list_lock_); - static std::string GetThreadGroupName(JDWP::ObjectId thread_group_id); - static JDWP::ObjectId GetThreadGroupParent(JDWP::ObjectId thread_group_id) + static JDWP::JdwpError GetThreadGroupName(JDWP::ObjectId thread_group_id, + JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static JDWP::JdwpError GetThreadGroupParent(JDWP::ObjectId thread_group_id, + JDWP::ExpandBuf* pReply) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + static JDWP::JdwpError GetThreadGroupChildren(JDWP::ObjectId thread_group_id, + JDWP::ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static JDWP::ObjectId GetSystemThreadGroupId() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static JDWP::ObjectId GetMainThreadGroupId(); static JDWP::JdwpThreadStatus ToJdwpThreadStatus(ThreadState state); static JDWP::JdwpError GetThreadStatus(JDWP::ObjectId thread_id, @@ -414,11 +420,9 @@ class Dbg { // Fills 'thread_ids' with the threads in the given thread group. If thread_group_id == 0, // returns all threads. - static void GetThreads(JDWP::ObjectId thread_group_id, std::vector<JDWP::ObjectId>* thread_ids) + static void GetThreads(mirror::Object* thread_group, std::vector<JDWP::ObjectId>* thread_ids) LOCKS_EXCLUDED(Locks::thread_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - static void GetChildThreadGroups(JDWP::ObjectId thread_group_id, - std::vector<JDWP::ObjectId>* child_thread_group_ids); static JDWP::JdwpError GetThreadFrameCount(JDWP::ObjectId thread_id, size_t* result) LOCKS_EXCLUDED(Locks::thread_list_lock_); diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index 64c9185c87..6a9976acb3 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -253,10 +253,10 @@ V(0xE8, IPUT_OBJECT_QUICK, "iput-object-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ V(0xE9, INVOKE_VIRTUAL_QUICK, "invoke-virtual-quick", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgNonZero | kVerifyRuntimeOnly) \ V(0xEA, INVOKE_VIRTUAL_RANGE_QUICK, "invoke-virtual/range-quick", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyVarArgRangeNonZero | kVerifyRuntimeOnly) \ - V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, false, kUnknown, 0, kVerifyError) \ - V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ - V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegAWide | kVerifyRegB | kVerifyRuntimeOnly) \ + V(0xEB, IPUT_BOOLEAN_QUICK, "iput-boolean-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ + V(0xEC, IPUT_BYTE_QUICK, "iput-byte-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ + V(0xED, IPUT_CHAR_QUICK, "iput-char-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ + V(0xEE, IPUT_SHORT_QUICK, "iput-short-quick", k22c, false, kFieldRef, kContinue | kThrow | kStore | kRegCFieldOrConstant, kVerifyRegA | kVerifyRegB | kVerifyRuntimeOnly) \ V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \ V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \ V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \ diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index ad22a2eae4..a7e5e7487b 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -569,7 +569,7 @@ RosAlloc::Run* RosAlloc::AllocRun(Thread* self, size_t idx) { RosAlloc::Run* RosAlloc::RefillRun(Thread* self, size_t idx) { // Get the lowest address non-full run from the binary tree. - std::set<Run*>* const bt = &non_full_runs_[idx]; + auto* const bt = &non_full_runs_[idx]; if (!bt->empty()) { // If there's one, use it as the current run. auto it = bt->begin(); @@ -767,7 +767,7 @@ size_t RosAlloc::FreeFromRun(Thread* self, void* ptr, Run* run) { } // Free the slot in the run. run->FreeSlot(ptr); - std::set<Run*>* non_full_runs = &non_full_runs_[idx]; + auto* non_full_runs = &non_full_runs_[idx]; if (run->IsAllFree()) { // It has just become completely free. Free the pages of this run. std::set<Run*>::iterator pos = non_full_runs->find(run); @@ -793,9 +793,8 @@ size_t RosAlloc::FreeFromRun(Thread* self, void* ptr, Run* run) { // already in the non-full run set (i.e., it was full) insert it // into the non-full run set. if (run != current_runs_[idx]) { - std::unordered_set<Run*, hash_run, eq_run>* full_runs = - kIsDebugBuild ? &full_runs_[idx] : NULL; - std::set<Run*>::iterator pos = non_full_runs->find(run); + auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL; + auto pos = non_full_runs->find(run); if (pos == non_full_runs->end()) { DCHECK(run_was_full); DCHECK(full_runs->find(run) != full_runs->end()); @@ -1266,9 +1265,8 @@ size_t RosAlloc::BulkFree(Thread* self, void** ptrs, size_t num_ptrs) { } // Check if the run should be moved to non_full_runs_ or // free_page_runs_. - std::set<Run*>* non_full_runs = &non_full_runs_[idx]; - std::unordered_set<Run*, hash_run, eq_run>* full_runs = - kIsDebugBuild ? &full_runs_[idx] : NULL; + auto* non_full_runs = &non_full_runs_[idx]; + auto* full_runs = kIsDebugBuild ? &full_runs_[idx] : NULL; if (run->IsAllFree()) { // It has just become completely free. Free the pages of the // run. @@ -2056,7 +2054,7 @@ void RosAlloc::Run::Verify(Thread* self, RosAlloc* rosalloc) { // in a run set. if (!is_current_run) { MutexLock mu(self, rosalloc->lock_); - std::set<Run*>& non_full_runs = rosalloc->non_full_runs_[idx]; + auto& non_full_runs = rosalloc->non_full_runs_[idx]; // If it's all free, it must be a free page run rather than a run. CHECK(!IsAllFree()) << "A free run must be in a free page run set " << Dump(); if (!IsFull()) { @@ -2066,7 +2064,7 @@ void RosAlloc::Run::Verify(Thread* self, RosAlloc* rosalloc) { } else { // If it's full, it must in the full run set (debug build only.) if (kIsDebugBuild) { - std::unordered_set<Run*, hash_run, eq_run>& full_runs = rosalloc->full_runs_[idx]; + auto& full_runs = rosalloc->full_runs_[idx]; CHECK(full_runs.find(this) != full_runs.end()) << " A full run isn't in the full run set " << Dump(); } diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index b2a5a3c96c..2fbd97a8d5 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -26,6 +26,7 @@ #include <unordered_set> #include <vector> +#include "base/allocator.h" #include "base/mutex.h" #include "base/logging.h" #include "globals.h" @@ -53,7 +54,7 @@ class RosAlloc { size_t pm_idx = rosalloc->ToPageMapIndex(fpr_base); size_t byte_size = rosalloc->free_page_run_size_map_[pm_idx]; DCHECK_GE(byte_size, static_cast<size_t>(0)); - DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0)); + DCHECK_ALIGNED(byte_size, kPageSize); return byte_size; } void SetByteSize(RosAlloc* rosalloc, size_t byte_size) @@ -403,6 +404,7 @@ class RosAlloc { // We use thread-local runs for the size Brackets whose indexes // are less than this index. We use shared (current) runs for the rest. + static const size_t kNumThreadLocalSizeBrackets = 11; private: @@ -423,12 +425,13 @@ class RosAlloc { // The run sets that hold the runs whose slots are not all // full. non_full_runs_[i] is guarded by size_bracket_locks_[i]. - std::set<Run*> non_full_runs_[kNumOfSizeBrackets]; + AllocationTrackingSet<Run*, kAllocatorTagRosAlloc> non_full_runs_[kNumOfSizeBrackets]; // The run sets that hold the runs whose slots are all full. This is // debug only. full_runs_[i] is guarded by size_bracket_locks_[i]. - std::unordered_set<Run*, hash_run, eq_run> full_runs_[kNumOfSizeBrackets]; + std::unordered_set<Run*, hash_run, eq_run, TrackingAllocator<Run*, kAllocatorTagRosAlloc>> + full_runs_[kNumOfSizeBrackets]; // The set of free pages. - std::set<FreePageRun*> free_page_runs_ GUARDED_BY(lock_); + AllocationTrackingSet<FreePageRun*, kAllocatorTagRosAlloc> free_page_runs_ GUARDED_BY(lock_); // The dedicated full run, it is always full and shared by all threads when revoking happens. // This is an optimization since enables us to avoid a null check for revoked runs. static Run* dedicated_full_run_; @@ -460,7 +463,8 @@ class RosAlloc { // The table that indicates the size of free page runs. These sizes // are stored here to avoid storing in the free page header and // release backing pages. - std::vector<size_t> free_page_run_size_map_ GUARDED_BY(lock_); + std::vector<size_t, TrackingAllocator<size_t, kAllocatorTagRosAlloc>> free_page_run_size_map_ + GUARDED_BY(lock_); // The global lock. Used to guard the page map, the free page set, // and the footprint. Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc index 40448524c6..b3bed64c5e 100644 --- a/runtime/gc/collector/mark_compact.cc +++ b/runtime/gc/collector/mark_compact.cc @@ -547,8 +547,11 @@ void MarkCompact::Sweep(bool swap_bitmaps) { } void MarkCompact::SweepLargeObjects(bool swap_bitmaps) { - TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings()); - RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps)); + space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace(); + if (los != nullptr) { + TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());\ + RecordFreeLOS(los->Sweep(swap_bitmaps)); + } } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc index 95530be202..930499a2fb 100644 --- a/runtime/gc/collector/mark_sweep.cc +++ b/runtime/gc/collector/mark_sweep.cc @@ -374,7 +374,8 @@ class MarkSweepMarkObjectSlowPath { } space::LargeObjectSpace* large_object_space = mark_sweep_->GetHeap()->GetLargeObjectsSpace(); if (UNLIKELY(obj == nullptr || !IsAligned<kPageSize>(obj) || - (kIsDebugBuild && !large_object_space->Contains(obj)))) { + (kIsDebugBuild && large_object_space != nullptr && + !large_object_space->Contains(obj)))) { LOG(ERROR) << "Tried to mark " << obj << " not contained by any spaces"; LOG(ERROR) << "Attempting see if it's a bad root"; mark_sweep_->VerifyRoots(); @@ -481,7 +482,7 @@ void MarkSweep::VerifyRoot(const Object* root, size_t vreg, const StackVisitor* // See if the root is on any space bitmap. if (heap_->GetLiveBitmap()->GetContinuousSpaceBitmap(root) == nullptr) { space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - if (!large_object_space->Contains(root)) { + if (large_object_space != nullptr && !large_object_space->Contains(root)) { LOG(ERROR) << "Found invalid root: " << root << " with type " << root_type; if (visitor != NULL) { LOG(ERROR) << visitor->DescribeLocation() << " in VReg: " << vreg; @@ -1074,20 +1075,22 @@ void MarkSweep::SweepArray(accounting::ObjectStack* allocations, bool swap_bitma } // Handle the large object space. space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap(); - accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap(); - if (swap_bitmaps) { - std::swap(large_live_objects, large_mark_objects); - } - for (size_t i = 0; i < count; ++i) { - Object* obj = objects[i]; - // Handle large objects. - if (kUseThreadLocalAllocationStack && obj == nullptr) { - continue; + if (large_object_space != nullptr) { + accounting::LargeObjectBitmap* large_live_objects = large_object_space->GetLiveBitmap(); + accounting::LargeObjectBitmap* large_mark_objects = large_object_space->GetMarkBitmap(); + if (swap_bitmaps) { + std::swap(large_live_objects, large_mark_objects); } - if (!large_mark_objects->Test(obj)) { - ++freed_los.objects; - freed_los.bytes += large_object_space->Free(self, obj); + for (size_t i = 0; i < count; ++i) { + Object* obj = objects[i]; + // Handle large objects. + if (kUseThreadLocalAllocationStack && obj == nullptr) { + continue; + } + if (!large_mark_objects->Test(obj)) { + ++freed_los.objects; + freed_los.bytes += large_object_space->Free(self, obj); + } } } { @@ -1125,8 +1128,11 @@ void MarkSweep::Sweep(bool swap_bitmaps) { } void MarkSweep::SweepLargeObjects(bool swap_bitmaps) { - TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings()); - RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps)); + space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace(); + if (los != nullptr) { + TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings()); + RecordFreeLOS(los->Sweep(swap_bitmaps)); + } } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index 8fb33cec2f..c8fa869a45 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -365,23 +365,23 @@ void SemiSpace::MarkReachableObjects() { } CHECK_EQ(is_large_object_space_immune_, collect_from_space_only_); - if (is_large_object_space_immune_) { + space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace(); + if (is_large_object_space_immune_ && los != nullptr) { TimingLogger::ScopedTiming t("VisitLargeObjects", GetTimings()); DCHECK(collect_from_space_only_); // Delay copying the live set to the marked set until here from // BindBitmaps() as the large objects on the allocation stack may // be newly added to the live set above in MarkAllocStackAsLive(). - GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked(); + los->CopyLiveToMarked(); // When the large object space is immune, we need to scan the // large object space as roots as they contain references to their // classes (primitive array classes) that could move though they // don't contain any other references. - space::LargeObjectSpace* large_object_space = GetHeap()->GetLargeObjectsSpace(); - accounting::LargeObjectBitmap* large_live_bitmap = large_object_space->GetLiveBitmap(); + accounting::LargeObjectBitmap* large_live_bitmap = los->GetLiveBitmap(); SemiSpaceScanObjectVisitor visitor(this); - large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(large_object_space->Begin()), - reinterpret_cast<uintptr_t>(large_object_space->End()), + large_live_bitmap->VisitMarkedRange(reinterpret_cast<uintptr_t>(los->Begin()), + reinterpret_cast<uintptr_t>(los->End()), visitor); } // Recursively process the mark stack. @@ -655,8 +655,11 @@ void SemiSpace::Sweep(bool swap_bitmaps) { void SemiSpace::SweepLargeObjects(bool swap_bitmaps) { DCHECK(!is_large_object_space_immune_); - TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings()); - RecordFreeLOS(heap_->GetLargeObjectsSpace()->Sweep(swap_bitmaps)); + space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace(); + if (los != nullptr) { + TimingLogger::ScopedTiming split("SweepLargeObjects", GetTimings()); + RecordFreeLOS(los->Sweep(swap_bitmaps)); + } } // Process the "referent" field in a java.lang.ref.Reference. If the referent has not yet been @@ -751,6 +754,7 @@ void SemiSpace::FinishPhase() { from_space_ = nullptr; CHECK(mark_stack_->IsEmpty()); mark_stack_->Reset(); + space::LargeObjectSpace* los = GetHeap()->GetLargeObjectsSpace(); if (generational_) { // Decide whether to do a whole heap collection or a bump pointer // only space collection at the next collection by updating @@ -762,7 +766,7 @@ void SemiSpace::FinishPhase() { bytes_promoted_since_last_whole_heap_collection_ += bytes_promoted_; bool bytes_promoted_threshold_exceeded = bytes_promoted_since_last_whole_heap_collection_ >= kBytesPromotedThreshold; - uint64_t current_los_bytes_allocated = GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated(); + uint64_t current_los_bytes_allocated = los != nullptr ? los->GetBytesAllocated() : 0U; uint64_t last_los_bytes_allocated = large_object_bytes_allocated_at_last_whole_heap_collection_; bool large_object_bytes_threshold_exceeded = @@ -775,7 +779,7 @@ void SemiSpace::FinishPhase() { // Reset the counters. bytes_promoted_since_last_whole_heap_collection_ = bytes_promoted_; large_object_bytes_allocated_at_last_whole_heap_collection_ = - GetHeap()->GetLargeObjectsSpace()->GetBytesAllocated(); + los != nullptr ? los->GetBytesAllocated() : 0U; collect_from_space_only_ = true; } } diff --git a/runtime/gc/collector/sticky_mark_sweep.cc b/runtime/gc/collector/sticky_mark_sweep.cc index 5a5844642b..4ed6abc386 100644 --- a/runtime/gc/collector/sticky_mark_sweep.cc +++ b/runtime/gc/collector/sticky_mark_sweep.cc @@ -16,7 +16,7 @@ #include "gc/heap.h" #include "gc/space/large_object_space.h" -#include "gc/space/space.h" +#include "gc/space/space-inl.h" #include "sticky_mark_sweep.h" #include "thread-inl.h" @@ -32,7 +32,6 @@ StickyMarkSweep::StickyMarkSweep(Heap* heap, bool is_concurrent, const std::stri void StickyMarkSweep::BindBitmaps() { PartialMarkSweep::BindBitmaps(); - WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_); // For sticky GC, we want to bind the bitmaps of all spaces as the allocation stack lets us // know what was allocated since the last GC. A side-effect of binding the allocation space mark @@ -44,7 +43,10 @@ void StickyMarkSweep::BindBitmaps() { space->AsContinuousMemMapAllocSpace()->BindLiveToMarkBitmap(); } } - GetHeap()->GetLargeObjectsSpace()->CopyLiveToMarked(); + for (const auto& space : GetHeap()->GetDiscontinuousSpaces()) { + CHECK(space->IsLargeObjectSpace()); + space->AsLargeObjectSpace()->CopyLiveToMarked(); + } } void StickyMarkSweep::MarkReachableObjects() { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index bb7da0d13d..18441c1cdf 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -83,13 +83,6 @@ static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB; // relative to partial/full GC. This may be desirable since sticky GCs interfere less with mutator // threads (lower pauses, use less memory bandwidth). static constexpr double kStickyGcThroughputAdjustment = 1.0; -// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR -// since this means that we have to use the slow msync loop in MemMap::MapAnonymous. -#if USE_ART_LOW_4G_ALLOCATOR -static constexpr bool kUseFreeListSpaceForLOS = true; -#else -static constexpr bool kUseFreeListSpaceForLOS = false; -#endif // Whether or not we compact the zygote in PreZygoteFork. static constexpr bool kCompactZygote = kMovingCollector; // How many reserve entries are at the end of the allocation stack, these are only needed if the @@ -107,8 +100,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max double target_utilization, double foreground_heap_growth_multiplier, size_t capacity, size_t non_moving_space_capacity, const std::string& image_file_name, const InstructionSet image_instruction_set, CollectorType foreground_collector_type, - CollectorType background_collector_type, size_t parallel_gc_threads, - size_t conc_gc_threads, bool low_memory_mode, + CollectorType background_collector_type, + space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold, + size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, bool verify_pre_sweeping_heap, bool verify_post_gc_heap, @@ -135,7 +129,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max ignore_max_footprint_(ignore_max_footprint), zygote_creation_lock_("zygote creation lock", kZygoteCreationLock), zygote_space_(nullptr), - large_object_threshold_(kDefaultLargeObjectThreshold), // Starts out disabled. + large_object_threshold_(large_object_threshold), collector_type_running_(kCollectorTypeNone), last_gc_type_(collector::kGcTypeNone), next_gc_type_(collector::kGcTypePartial), @@ -338,13 +332,21 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max CHECK(non_moving_space_ != nullptr); CHECK(!non_moving_space_->CanMoveObjects()); // Allocate the large object space. - if (kUseFreeListSpaceForLOS) { - large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity_); + if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) { + large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr, + capacity_); + CHECK(large_object_space_ != nullptr) << "Failed to create large object space"; + } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) { + large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space"); + CHECK(large_object_space_ != nullptr) << "Failed to create large object space"; } else { - large_object_space_ = space::LargeObjectMapSpace::Create("large object space"); + // Disable the large object space by making the cutoff excessively large. + large_object_threshold_ = std::numeric_limits<size_t>::max(); + large_object_space_ = nullptr; + } + if (large_object_space_ != nullptr) { + AddSpace(large_object_space_); } - CHECK(large_object_space_ != nullptr) << "Failed to create large object space"; - AddSpace(large_object_space_); // Compute heap capacity. Continuous spaces are sorted in order of Begin(). CHECK(!continuous_spaces_.empty()); // Relies on the spaces being sorted. @@ -424,7 +426,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } if (running_on_valgrind_) { - Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints(false); } if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() exiting"; @@ -712,7 +714,8 @@ void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) { CHECK(space1 != nullptr); CHECK(space2 != nullptr); MarkAllocStack(space1->GetLiveBitmap(), space2->GetLiveBitmap(), - large_object_space_->GetLiveBitmap(), stack); + (large_object_space_ != nullptr ? large_object_space_->GetLiveBitmap() : nullptr), + stack); } void Heap::DeleteThreadPool() { @@ -1002,7 +1005,10 @@ void Heap::Trim() { total_alloc_space_size += malloc_space->Size(); } } - total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated(); + total_alloc_space_allocated = GetBytesAllocated(); + if (large_object_space_ != nullptr) { + total_alloc_space_allocated -= large_object_space_->GetBytesAllocated(); + } if (bump_pointer_space_ != nullptr) { total_alloc_space_allocated -= bump_pointer_space_->Size(); } @@ -2018,6 +2024,7 @@ void Heap::MarkAllocStack(accounting::ContinuousSpaceBitmap* bitmap1, } else if (bitmap2->HasAddress(obj)) { bitmap2->Set(obj); } else { + DCHECK(large_objects != nullptr); large_objects->Set(obj); } } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 351e1c6b11..faaea4077c 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -30,6 +30,7 @@ #include "gc/collector/garbage_collector.h" #include "gc/collector/gc_type.h" #include "gc/collector_type.h" +#include "gc/space/large_object_space.h" #include "globals.h" #include "gtest/gtest.h" #include "instruction_set.h" @@ -129,8 +130,6 @@ class Heap { public: // If true, measure the total allocation time. static constexpr bool kMeasureAllocationTime = false; - // Primitive arrays larger than this size are put in the large object space. - static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize; static constexpr size_t kDefaultStartingSize = kPageSize; static constexpr size_t kDefaultInitialSize = 2 * MB; static constexpr size_t kDefaultMaximumSize = 256 * MB; @@ -142,7 +141,17 @@ class Heap { static constexpr size_t kDefaultTLABSize = 256 * KB; static constexpr double kDefaultTargetUtilization = 0.5; static constexpr double kDefaultHeapGrowthMultiplier = 2.0; - + // Primitive arrays larger than this size are put in the large object space. + static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize; + // Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR + // since this means that we have to use the slow msync loop in MemMap::MapAnonymous. +#if USE_ART_LOW_4G_ALLOCATOR + static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType = + space::kLargeObjectSpaceTypeFreeList; +#else + static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType = + space::kLargeObjectSpaceTypeMap; +#endif // Used so that we don't overflow the allocation time atomic integer. static constexpr size_t kTimeAdjust = 1024; @@ -161,6 +170,7 @@ class Heap { const std::string& original_image_file_name, InstructionSet image_instruction_set, CollectorType foreground_collector_type, CollectorType background_collector_type, + space::LargeObjectSpaceType large_object_space_type, size_t large_object_threshold, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_threshold, size_t long_gc_threshold, bool ignore_max_footprint, bool use_tlab, diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc index 2a4371290f..dad5855748 100644 --- a/runtime/gc/space/large_object_space.cc +++ b/runtime/gc/space/large_object_space.cc @@ -120,7 +120,7 @@ mirror::Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes, mirror::Object* obj = reinterpret_cast<mirror::Object*>(mem_map->Begin()); large_objects_.push_back(obj); mem_maps_.Put(obj, mem_map); - size_t allocation_size = mem_map->Size(); + const size_t allocation_size = mem_map->BaseSize(); DCHECK(bytes_allocated != nullptr); begin_ = std::min(begin_, reinterpret_cast<byte*>(obj)); byte* obj_end = reinterpret_cast<byte*>(obj) + allocation_size; @@ -145,8 +145,9 @@ size_t LargeObjectMapSpace::Free(Thread* self, mirror::Object* ptr) { Runtime::Current()->GetHeap()->DumpSpaces(LOG(ERROR)); LOG(FATAL) << "Attempted to free large object " << ptr << " which was not live"; } - DCHECK_GE(num_bytes_allocated_, found->second->Size()); - size_t allocation_size = found->second->Size(); + const size_t map_size = found->second->BaseSize(); + DCHECK_GE(num_bytes_allocated_, map_size); + size_t allocation_size = map_size; num_bytes_allocated_ -= allocation_size; --num_objects_allocated_; delete found->second; @@ -158,7 +159,7 @@ size_t LargeObjectMapSpace::AllocationSize(mirror::Object* obj, size_t* usable_s MutexLock mu(Thread::Current(), lock_); auto found = mem_maps_.find(obj); CHECK(found != mem_maps_.end()) << "Attempted to get size of a large object which is not live"; - return found->second->Size(); + return found->second->BaseSize(); } size_t LargeObjectSpace::FreeList(Thread* self, size_t num_ptrs, mirror::Object** ptrs) { diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index 9d5e090a29..a63c5c0aae 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -31,6 +31,12 @@ namespace space { class AllocationInfo; +enum LargeObjectSpaceType { + kLargeObjectSpaceTypeDisabled, + kLargeObjectSpaceTypeMap, + kLargeObjectSpaceTypeFreeList, +}; + // Abstraction implemented by all large object spaces. class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { public: diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 0f45b9e512..a2e88a694e 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -597,10 +597,13 @@ static void ResetQuickAllocEntryPointsForThread(Thread* thread, void* arg) { thread->ResetQuickAllocEntryPointsForThread(); } -void Instrumentation::SetEntrypointsInstrumented(bool instrumented) { +void Instrumentation::SetEntrypointsInstrumented(bool instrumented, bool suspended) { Runtime* runtime = Runtime::Current(); ThreadList* tl = runtime->GetThreadList(); - if (runtime->IsStarted()) { + if (suspended) { + Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current()); + } + if (runtime->IsStarted() && !suspended) { tl->SuspendAll(); } { @@ -608,30 +611,30 @@ void Instrumentation::SetEntrypointsInstrumented(bool instrumented) { SetQuickAllocEntryPointsInstrumented(instrumented); ResetQuickAllocEntryPoints(); } - if (runtime->IsStarted()) { + if (runtime->IsStarted() && !suspended) { tl->ResumeAll(); } } -void Instrumentation::InstrumentQuickAllocEntryPoints() { +void Instrumentation::InstrumentQuickAllocEntryPoints(bool suspended) { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. DCHECK_GE(quick_alloc_entry_points_instrumentation_counter_.LoadSequentiallyConsistent(), 0); const bool enable_instrumentation = quick_alloc_entry_points_instrumentation_counter_.FetchAndAddSequentiallyConsistent(1) == 0; if (enable_instrumentation) { - SetEntrypointsInstrumented(true); + SetEntrypointsInstrumented(true, suspended); } } -void Instrumentation::UninstrumentQuickAllocEntryPoints() { +void Instrumentation::UninstrumentQuickAllocEntryPoints(bool suspended) { // TODO: the read of quick_alloc_entry_points_instrumentation_counter_ is racey and this code // should be guarded by a lock. DCHECK_GT(quick_alloc_entry_points_instrumentation_counter_.LoadSequentiallyConsistent(), 0); const bool disable_instrumentation = quick_alloc_entry_points_instrumentation_counter_.FetchAndSubSequentiallyConsistent(1) == 1; if (disable_instrumentation) { - SetEntrypointsInstrumented(false); + SetEntrypointsInstrumented(false, suspended); } } diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h index d05cee5dcb..3c1c756992 100644 --- a/runtime/instrumentation.h +++ b/runtime/instrumentation.h @@ -182,10 +182,10 @@ class Instrumentation { return interpreter_handler_table_; } - void InstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_, - Locks::runtime_shutdown_lock_); - void UninstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_, - Locks::runtime_shutdown_lock_); + void InstrumentQuickAllocEntryPoints(bool suspended) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::runtime_shutdown_lock_); + void UninstrumentQuickAllocEntryPoints(bool suspended) + LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::runtime_shutdown_lock_); void ResetQuickAllocEntryPoints() EXCLUSIVE_LOCKS_REQUIRED(Locks::runtime_shutdown_lock_); // Update the code of a method respecting any installed stubs. @@ -350,7 +350,7 @@ class Instrumentation { // No thread safety analysis to get around SetQuickAllocEntryPointsInstrumented requiring // exclusive access to mutator lock which you can't get if the runtime isn't started. - void SetEntrypointsInstrumented(bool instrumented) NO_THREAD_SAFETY_ANALYSIS; + void SetEntrypointsInstrumented(bool instrumented, bool suspended) NO_THREAD_SAFETY_ANALYSIS; void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, mirror::ArtMethod* method, uint32_t dex_pc) const diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc index 35095f95ca..e0a83f607b 100644 --- a/runtime/jdwp/jdwp_handler.cc +++ b/runtime/jdwp/jdwp_handler.cc @@ -151,7 +151,12 @@ static JdwpError FinishInvoke(JdwpState*, Request* request, ExpandBuf* pReply, /* show detailed debug output */ if (resultTag == JT_STRING && exceptObjId == 0) { if (resultValue != 0) { - VLOG(jdwp) << " string '" << Dbg::StringToUtf8(resultValue) << "'"; + if (VLOG_IS_ON(jdwp)) { + std::string result_string; + JDWP::JdwpError error = Dbg::StringToUtf8(resultValue, &result_string); + CHECK_EQ(error, JDWP::ERR_NONE); + VLOG(jdwp) << " string '" << result_string << "'"; + } } else { VLOG(jdwp) << " string (null)"; } @@ -220,7 +225,7 @@ static JdwpError VM_ClassesBySignature(JdwpState*, Request* request, ExpandBuf* static JdwpError VM_AllThreads(JdwpState*, Request*, ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { std::vector<ObjectId> thread_ids; - Dbg::GetThreads(0, &thread_ids); + Dbg::GetThreads(nullptr /* all thread groups */, &thread_ids); expandBufAdd4BE(pReply, thread_ids.size()); for (uint32_t i = 0; i < thread_ids.size(); ++i) { @@ -919,7 +924,11 @@ static JdwpError OR_ReferringObjects(JdwpState*, Request* request, ExpandBuf* re static JdwpError SR_Value(JdwpState*, Request* request, ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectId stringObject = request->ReadObjectId(); - std::string str(Dbg::StringToUtf8(stringObject)); + std::string str; + JDWP::JdwpError error = Dbg::StringToUtf8(stringObject, &str); + if (error != JDWP::ERR_NONE) { + return error; + } VLOG(jdwp) << StringPrintf(" --> %s", PrintableString(str.c_str()).c_str()); @@ -1141,10 +1150,7 @@ static JdwpError TR_DebugSuspendCount(JdwpState*, Request* request, ExpandBuf* p static JdwpError TGR_Name(JdwpState*, Request* request, ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectId thread_group_id = request->ReadThreadGroupId(); - - expandBufAddUtf8String(pReply, Dbg::GetThreadGroupName(thread_group_id)); - - return ERR_NONE; + return Dbg::GetThreadGroupName(thread_group_id, pReply); } /* @@ -1154,11 +1160,7 @@ static JdwpError TGR_Name(JdwpState*, Request* request, ExpandBuf* pReply) static JdwpError TGR_Parent(JdwpState*, Request* request, ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectId thread_group_id = request->ReadThreadGroupId(); - - ObjectId parentGroup = Dbg::GetThreadGroupParent(thread_group_id); - expandBufAddObjectId(pReply, parentGroup); - - return ERR_NONE; + return Dbg::GetThreadGroupParent(thread_group_id, pReply); } /* @@ -1168,22 +1170,7 @@ static JdwpError TGR_Parent(JdwpState*, Request* request, ExpandBuf* pReply) static JdwpError TGR_Children(JdwpState*, Request* request, ExpandBuf* pReply) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ObjectId thread_group_id = request->ReadThreadGroupId(); - - std::vector<ObjectId> thread_ids; - Dbg::GetThreads(thread_group_id, &thread_ids); - expandBufAdd4BE(pReply, thread_ids.size()); - for (uint32_t i = 0; i < thread_ids.size(); ++i) { - expandBufAddObjectId(pReply, thread_ids[i]); - } - - std::vector<ObjectId> child_thread_groups_ids; - Dbg::GetChildThreadGroups(thread_group_id, &child_thread_groups_ids); - expandBufAdd4BE(pReply, child_thread_groups_ids.size()); - for (uint32_t i = 0; i < child_thread_groups_ids.size(); ++i) { - expandBufAddObjectId(pReply, child_thread_groups_ids[i]); - } - - return ERR_NONE; + return Dbg::GetThreadGroupChildren(thread_group_id, pReply); } /* diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index ae170702b5..8447616cf5 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -285,14 +285,17 @@ inline const uint8_t* ArtMethod::GetVmapTable(const void* code_pointer) { } inline StackMap ArtMethod::GetStackMap(uint32_t native_pc_offset) { + return GetOptimizedCodeInfo().GetStackMapForNativePcOffset(native_pc_offset); +} + +inline CodeInfo ArtMethod::GetOptimizedCodeInfo() { DCHECK(IsOptimized()); const void* code_pointer = GetQuickOatCodePointer(); DCHECK(code_pointer != nullptr); uint32_t offset = reinterpret_cast<const OatQuickMethodHeader*>(code_pointer)[-1].vmap_table_offset_; const void* data = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(code_pointer) - offset); - CodeInfo code_info(data); - return code_info.GetStackMapForNativePcOffset(native_pc_offset); + return CodeInfo(data); } inline void ArtMethod::SetOatNativeGcMapOffset(uint32_t gc_map_offset) { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index d37aa57688..de6ec05442 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -155,7 +155,9 @@ class MANAGED ArtMethod FINAL : public Object { // Temporary solution for detecting if a method has been optimized: the compiler // does not create a GC map. Instead, the vmap table contains the stack map // (as in stack_map.h). - return (GetEntryPointFromQuickCompiledCode() != nullptr) && (GetNativeGcMap() == nullptr); + return (GetEntryPointFromQuickCompiledCode() != nullptr) + && (GetQuickOatCodePointer() != nullptr) + && (GetNativeGcMap() == nullptr); } bool IsPortableCompiled() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -349,6 +351,7 @@ class MANAGED ArtMethod FINAL : public Object { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); StackMap GetStackMap(uint32_t native_pc_offset) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + CodeInfo GetOptimizedCodeInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const uint8_t* GetNativeGcMap() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return GetFieldPtr<uint8_t*>(OFFSET_OF_OBJECT_MEMBER(ArtMethod, gc_map_)); diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc index ceff2065ba..d8a537f948 100644 --- a/runtime/native/dalvik_system_VMDebug.cc +++ b/runtime/native/dalvik_system_VMDebug.cc @@ -60,11 +60,11 @@ static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { } static void VMDebug_startAllocCounting(JNIEnv*, jclass) { - Runtime::Current()->SetStatsEnabled(true); + Runtime::Current()->SetStatsEnabled(true, false); } static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { - Runtime::Current()->SetStatsEnabled(false); + Runtime::Current()->SetStatsEnabled(false, false); } static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h index 97ca6b2b37..9570bb501a 100644 --- a/runtime/oat_file-inl.h +++ b/runtime/oat_file-inl.h @@ -21,6 +21,39 @@ namespace art { +inline const OatQuickMethodHeader* OatFile::OatMethod::GetOatQuickMethodHeader() const { + const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode()); + if (code == nullptr) { + return nullptr; + } + // Return a pointer to the packed struct before the code. + return reinterpret_cast<const OatQuickMethodHeader*>(code) - 1; +} + +inline uint32_t OatFile::OatMethod::GetOatQuickMethodHeaderOffset() const { + const OatQuickMethodHeader* method_header = GetOatQuickMethodHeader(); + if (method_header == nullptr) { + return 0u; + } + return reinterpret_cast<const byte*>(method_header) - begin_; +} + +inline uint32_t OatFile::OatMethod::GetQuickCodeSize() const { + const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode()); + if (code == nullptr) { + return 0u; + } + return reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].code_size_; +} + +inline uint32_t OatFile::OatMethod::GetQuickCodeSizeOffset() const { + const OatQuickMethodHeader* method_header = GetOatQuickMethodHeader(); + if (method_header == nullptr) { + return 0u; + } + return reinterpret_cast<const byte*>(&method_header->code_size_) - begin_; +} + inline size_t OatFile::OatMethod::GetFrameSizeInBytes() const { const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode()); if (code == nullptr) { @@ -50,11 +83,27 @@ inline uint32_t OatFile::OatMethod::GetMappingTableOffset() const { return static_cast<uint32_t>(mapping_table != nullptr ? mapping_table - begin_ : 0u); } +inline uint32_t OatFile::OatMethod::GetMappingTableOffsetOffset() const { + const OatQuickMethodHeader* method_header = GetOatQuickMethodHeader(); + if (method_header == nullptr) { + return 0u; + } + return reinterpret_cast<const byte*>(&method_header->mapping_table_offset_) - begin_; +} + inline uint32_t OatFile::OatMethod::GetVmapTableOffset() const { const uint8_t* vmap_table = GetVmapTable(); return static_cast<uint32_t>(vmap_table != nullptr ? vmap_table - begin_ : 0u); } +inline uint32_t OatFile::OatMethod::GetVmapTableOffsetOffset() const { + const OatQuickMethodHeader* method_header = GetOatQuickMethodHeader(); + if (method_header == nullptr) { + return 0u; + } + return reinterpret_cast<const byte*>(&method_header->vmap_table_offset_) - begin_; +} + inline const uint8_t* OatFile::OatMethod::GetMappingTable() const { const void* code = mirror::ArtMethod::EntryPointToCodePointer(GetQuickCode()); if (code == nullptr) { diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c621e88111..a896f3eece 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -454,8 +454,12 @@ const DexFile* OatFile::OatDexFile::OpenDexFile(std::string* error_msg) const { dex_file_location_checksum_, error_msg); } +uint32_t OatFile::OatDexFile::GetOatClassOffset(uint16_t class_def_index) const { + return oat_class_offsets_pointer_[class_def_index]; +} + OatFile::OatClass OatFile::OatDexFile::GetOatClass(uint16_t class_def_index) const { - uint32_t oat_class_offset = oat_class_offsets_pointer_[class_def_index]; + uint32_t oat_class_offset = GetOatClassOffset(class_def_index); const byte* oat_class_pointer = oat_file_->Begin() + oat_class_offset; CHECK_LT(oat_class_pointer, oat_file_->End()) << oat_file_->GetLocation(); @@ -531,49 +535,54 @@ OatFile::OatClass::OatClass(const OatFile* oat_file, } } -const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const { +uint32_t OatFile::OatClass::GetOatMethodOffsetsOffset(uint32_t method_index) const { + const OatMethodOffsets* oat_method_offsets = GetOatMethodOffsets(method_index); + if (oat_method_offsets == nullptr) { + return 0u; + } + return reinterpret_cast<const uint8_t*>(oat_method_offsets) - oat_file_->Begin(); +} + +const OatMethodOffsets* OatFile::OatClass::GetOatMethodOffsets(uint32_t method_index) const { // NOTE: We don't keep the number of methods and cannot do a bounds check for method_index. - if (methods_pointer_ == NULL) { + if (methods_pointer_ == nullptr) { CHECK_EQ(kOatClassNoneCompiled, type_); - return OatMethod(NULL, 0, 0); + return nullptr; } size_t methods_pointer_index; - if (bitmap_ == NULL) { + if (bitmap_ == nullptr) { CHECK_EQ(kOatClassAllCompiled, type_); methods_pointer_index = method_index; } else { CHECK_EQ(kOatClassSomeCompiled, type_); if (!BitVector::IsBitSet(bitmap_, method_index)) { - return OatMethod(NULL, 0, 0); + return nullptr; } size_t num_set_bits = BitVector::NumSetBits(bitmap_, method_index); methods_pointer_index = num_set_bits; } const OatMethodOffsets& oat_method_offsets = methods_pointer_[methods_pointer_index]; - if (oat_file_->IsExecutable() - || (Runtime::Current() == nullptr) - || Runtime::Current()->IsCompiler()) { + return &oat_method_offsets; +} + +const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) const { + const OatMethodOffsets* oat_method_offsets = GetOatMethodOffsets(method_index); + if (oat_method_offsets == nullptr) { + return OatMethod(nullptr, 0, 0); + } + if (oat_file_->IsExecutable() || + Runtime::Current() == nullptr || // This case applies for oatdump. + Runtime::Current()->IsCompiler()) { return OatMethod( oat_file_->Begin(), - oat_method_offsets.code_offset_, - oat_method_offsets.gc_map_offset_); + oat_method_offsets->code_offset_, + oat_method_offsets->gc_map_offset_); } else { // We aren't allowed to use the compiled code. We just force it down the interpreted version. return OatMethod(oat_file_->Begin(), 0, 0); } } - -uint32_t OatFile::OatMethod::GetQuickCodeSize() const { - uintptr_t code = reinterpret_cast<uintptr_t>(GetQuickCode()); - if (code == 0) { - return 0; - } - // TODO: make this Thumb2 specific - code &= ~0x1; - return reinterpret_cast<uint32_t*>(code)[-1]; -} - void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const { CHECK(method != NULL); method->SetEntryPointFromPortableCompiledCode(GetPortableCode()); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 2fd4f4c98a..b9d5702b0f 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -114,13 +114,22 @@ class OatFile { } } + // Returns 0. uint32_t GetPortableCodeSize() const { // TODO: With Quick, we store the size before the code. With Portable, the code is in a .o // file we don't manage ourselves. ELF symbols do have a concept of size, so we could capture // that and store it somewhere, such as the OatMethod. return 0; } + + // Returns size of quick code. uint32_t GetQuickCodeSize() const; + uint32_t GetQuickCodeSizeOffset() const; + + // Returns OatQuickMethodHeader for debugging. Most callers should + // use more specific methods such as GetQuickCodeSize. + const OatQuickMethodHeader* GetOatQuickMethodHeader() const; + uint32_t GetOatQuickMethodHeaderOffset() const; const uint8_t* GetNativeGcMap() const { return GetOatPointer<const uint8_t*>(native_gc_map_offset_); @@ -129,10 +138,14 @@ class OatFile { size_t GetFrameSizeInBytes() const; uint32_t GetCoreSpillMask() const; uint32_t GetFpSpillMask() const; - uint32_t GetMappingTableOffset() const; - uint32_t GetVmapTableOffset() const; + const uint8_t* GetMappingTable() const; + uint32_t GetMappingTableOffset() const; + uint32_t GetMappingTableOffsetOffset() const; + const uint8_t* GetVmapTable() const; + uint32_t GetVmapTableOffset() const; + uint32_t GetVmapTableOffsetOffset() const; // Create an OatMethod with offsets relative to the given base address OatMethod(const byte* base, const uint32_t code_offset, const uint32_t gc_map_offset) @@ -176,11 +189,21 @@ class OatFile { } // Get the OatMethod entry based on its index into the class - // defintion. direct methods come first, followed by virtual - // methods. note that runtime created methods such as miranda + // defintion. Direct methods come first, followed by virtual + // methods. Note that runtime created methods such as miranda // methods are not included. const OatMethod GetOatMethod(uint32_t method_index) const; + // Return a pointer to the OatMethodOffsets for the requested + // method_index, or nullptr if none is present. Note that most + // callers should use GetOatMethod. + const OatMethodOffsets* GetOatMethodOffsets(uint32_t method_index) const; + + // Return the offset from the start of the OatFile to the + // OatMethodOffsets for the requested method_index, or 0 if none + // is present. Note that most callers should use GetOatMethod. + uint32_t GetOatMethodOffsetsOffset(uint32_t method_index) const; + // A representation of an invalid OatClass, used when an OatClass can't be found. // See ClassLinker::FindOatClass. static OatClass Invalid() { @@ -239,6 +262,9 @@ class OatFile { // Returns the OatClass for the class specified by the given DexFile class_def_index. OatClass GetOatClass(uint16_t class_def_index) const; + // Returns the offset to the OatClass information. Most callers should use GetOatClass. + uint32_t GetOatClassOffset(uint16_t class_def_index) const; + ~OatDexFile(); private: diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 37e08a57ea..6b4f764d6e 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -63,6 +63,8 @@ ParsedOptions::ParsedOptions() heap_min_free_(gc::Heap::kDefaultMinFree), heap_max_free_(gc::Heap::kDefaultMaxFree), heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity), + large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType), + large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold), heap_target_utilization_(gc::Heap::kDefaultTargetUtilization), foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier), parallel_gc_threads_(1), @@ -452,6 +454,32 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize if (!ParseXGcOption(option)) { return false; } + } else if (StartsWith(option, "-XX:LargeObjectSpace=")) { + std::string substring; + if (!ParseStringAfterChar(option, '=', &substring)) { + return false; + } + if (substring == "disabled") { + large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled; + } else if (substring == "freelist") { + large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList; + } else if (substring == "map") { + large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap; + } else { + Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str()); + return false; + } + } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) { + std::string substring; + if (!ParseStringAfterChar(option, '=', &substring)) { + return false; + } + size_t size = ParseMemoryOption(substring.c_str(), 1); + if (size == 0) { + Usage("Failed to parse memory option %s\n", option.c_str()); + return false; + } + large_object_threshold_ = size; } else if (StartsWith(option, "-XX:BackgroundGC=")) { std::string substring; if (!ParseStringAfterChar(option, '=', &substring)) { @@ -757,7 +785,6 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xstacktracefile:<filename>\n"); UsageMessage(stream, " -Xgc:[no]preverify\n"); UsageMessage(stream, " -Xgc:[no]postverify\n"); - UsageMessage(stream, " -XX:+DisableExplicitGC\n"); UsageMessage(stream, " -XX:HeapGrowthLimit=N\n"); UsageMessage(stream, " -XX:HeapMinFree=N\n"); UsageMessage(stream, " -XX:HeapMaxFree=N\n"); @@ -774,6 +801,7 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -Xgc:[no]postverify_rosalloc\n"); UsageMessage(stream, " -Xgc:[no]presweepingverify\n"); UsageMessage(stream, " -Ximage:filename\n"); + UsageMessage(stream, " -XX:+DisableExplicitGC\n"); UsageMessage(stream, " -XX:ParallelGCThreads=integervalue\n"); UsageMessage(stream, " -XX:ConcGCThreads=integervalue\n"); UsageMessage(stream, " -XX:MaxSpinsBeforeThinLockInflation=integervalue\n"); @@ -783,6 +811,8 @@ void ParsedOptions::Usage(const char* fmt, ...) { UsageMessage(stream, " -XX:IgnoreMaxFootprint\n"); UsageMessage(stream, " -XX:UseTLAB\n"); UsageMessage(stream, " -XX:BackgroundGC=none\n"); + UsageMessage(stream, " -XX:LargeObjectSpace={disabled,map,freelist}\n"); + UsageMessage(stream, " -XX:LargeObjectThreshold=N\n"); UsageMessage(stream, " -Xmethod-trace\n"); UsageMessage(stream, " -Xmethod-trace-file:filename"); UsageMessage(stream, " -Xmethod-trace-file-size:integervalue\n"); diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h index 3839e19940..26a2f31b13 100644 --- a/runtime/parsed_options.h +++ b/runtime/parsed_options.h @@ -24,6 +24,7 @@ #include "globals.h" #include "gc/collector_type.h" +#include "gc/space/large_object_space.h" #include "instruction_set.h" #include "profiler_options.h" @@ -72,6 +73,8 @@ class ParsedOptions { size_t heap_min_free_; size_t heap_max_free_; size_t heap_non_moving_space_capacity_; + gc::space::LargeObjectSpaceType large_object_space_type_; + size_t large_object_threshold_; double heap_target_utilization_; double foreground_heap_growth_multiplier_; unsigned int parallel_gc_threads_; diff --git a/runtime/runtime.cc b/runtime/runtime.cc index a1ea3cf5bf..0e382ff65a 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -697,6 +697,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) options->image_isa_, options->collector_type_, options->background_collector_type_, + options->large_object_space_type_, + options->large_object_threshold_, options->parallel_gc_threads_, options->conc_gc_threads_, options->low_memory_mode_, @@ -996,14 +998,14 @@ void Runtime::DumpLockHolders(std::ostream& os) { } } -void Runtime::SetStatsEnabled(bool new_state) { +void Runtime::SetStatsEnabled(bool new_state, bool suspended) { if (new_state == true) { GetStats()->Clear(~0); // TODO: wouldn't it make more sense to clear _all_ threads' stats? Thread::Current()->GetStats()->Clear(~0); - GetInstrumentation()->InstrumentQuickAllocEntryPoints(); + GetInstrumentation()->InstrumentQuickAllocEntryPoints(suspended); } else { - GetInstrumentation()->UninstrumentQuickAllocEntryPoints(); + GetInstrumentation()->UninstrumentQuickAllocEntryPoints(suspended); } stats_enabled_ = new_state; } diff --git a/runtime/runtime.h b/runtime/runtime.h index cfb1abc477..f9c017b278 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -390,7 +390,7 @@ class Runtime { void ResetStats(int kinds); - void SetStatsEnabled(bool new_state); + void SetStatsEnabled(bool new_state, bool suspended); enum class NativeBridgeAction { // private kUnload, diff --git a/runtime/stack.h b/runtime/stack.h index 8e5da3587d..44e36c478e 100644 --- a/runtime/stack.h +++ b/runtime/stack.h @@ -604,8 +604,8 @@ class StackVisitor { * | Compiler temp region | ... (reg >= max_num_special_temps) * | . | * | . | - * | V[max_num_special_temps + 1] | - * | V[max_num_special_temps + 0] | + * | V[max_num_special_temps + 1] | + * | V[max_num_special_temps + 0] | * +-------------------------------+ * | OUT[outs-1] | * | OUT[outs-2] | diff --git a/runtime/thread.cc b/runtime/thread.cc index c54bebe1ce..650b0f95d4 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -961,7 +961,7 @@ void Thread::DumpStack(std::ostream& os) const { // If we're currently in native code, dump that stack before dumping the managed stack. if (dump_for_abort || ShouldShowNativeStack(this)) { DumpKernelStack(os, GetTid(), " kernel: ", false); - DumpNativeStack(os, GetTid(), " native: ", GetCurrentMethod(nullptr)); + DumpNativeStack(os, GetTid(), " native: ", GetCurrentMethod(nullptr, !dump_for_abort)); } DumpJavaStack(os); } else { diff --git a/runtime/trace.cc b/runtime/trace.cc index 6dcc5fe6bb..b32e0429b1 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -373,11 +373,9 @@ void Trace::Start(const char* trace_filename, int trace_fd, int buffer_size, int // Enable count of allocs if specified in the flags. if ((flags && kTraceCountAllocs) != 0) { - runtime->SetStatsEnabled(true); + runtime->SetStatsEnabled(true, true); } - - if (sampling_enabled) { CHECK_PTHREAD_CALL(pthread_create, (&sampling_pthread_, NULL, &RunSamplingThread, reinterpret_cast<void*>(interval_us)), @@ -492,7 +490,7 @@ void Trace::FinishTracing() { size_t final_offset = cur_offset_.LoadRelaxed(); if ((flags_ & kTraceCountAllocs) != 0) { - Runtime::Current()->SetStatsEnabled(false); + Runtime::Current()->SetStatsEnabled(false, true); } std::set<mirror::ArtMethod*> visited_methods; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f24128137e..f28d4883b2 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -311,9 +311,15 @@ MethodVerifier* MethodVerifier::VerifyMethodAndDump(Thread* self, std::ostream& verifier->Verify(); verifier->DumpFailures(os); os << verifier->info_messages_.str(); - verifier->Dump(os); - - return verifier; + // Only dump and return if no hard failures. Otherwise the verifier may be not fully initialized + // and querying any info is dangerous/can abort. + if (verifier->have_pending_hard_failure_) { + delete verifier; + return nullptr; + } else { + verifier->Dump(os); + return verifier; + } } MethodVerifier::MethodVerifier(Thread* self, @@ -3345,8 +3351,8 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst } mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instruction* inst, - bool is_range) { - DCHECK(Runtime::Current()->IsStarted()); + bool is_range) { + DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); mirror::ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(), is_range); if (res_method == nullptr) { @@ -3861,7 +3867,7 @@ mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& insn_type, bool is_primitive) { - DCHECK(Runtime::Current()->IsStarted()); + DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); if (field == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); @@ -3920,7 +3926,7 @@ void MethodVerifier::VerifyIGetQuick(const Instruction* inst, const RegType& ins void MethodVerifier::VerifyIPutQuick(const Instruction* inst, const RegType& insn_type, bool is_primitive) { - DCHECK(Runtime::Current()->IsStarted()); + DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); if (field == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc index 79295549c9..e914bd9d09 100644 --- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc +++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc @@ -14,138 +14,57 @@ * limitations under the License. */ -#include <stdio.h> -#include <memory> - -#include "class_linker.h" -#include "dex_file-inl.h" -#include "gc_map.h" -#include "mirror/art_method-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/object-inl.h" -#include "scoped_thread_state_change.h" -#include "thread.h" +#include "check_reference_map_visitor.h" #include "jni.h" -#include "verifier/method_verifier.h" namespace art { -#define IS_IN_REF_BITMAP(ref_bitmap, reg) \ - (((reg) < m->GetCodeItem()->registers_size_) && \ - ((*((ref_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01)) - -#define CHECK_REGS_CONTAIN_REFS(...) \ - do { \ - int t[] = {__VA_ARGS__}; \ - int t_size = sizeof(t) / sizeof(*t); \ - for (int i = 0; i < t_size; ++i) \ - CHECK(IS_IN_REF_BITMAP(ref_bitmap, t[i])) \ - << "Error: Reg @ " << i << "-th argument is not in GC map"; \ - } while (false) +#define CHECK_REGS_CONTAIN_REFS(native_pc_offset, ...) do { \ + int t[] = {__VA_ARGS__}; \ + int t_size = sizeof(t) / sizeof(*t); \ + CheckReferences(t, t_size, m->NativePcOffset(m->ToNativePc(native_pc_offset))); \ +} while (false); -struct ReferenceMap2Visitor : public StackVisitor { - explicit ReferenceMap2Visitor(Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, NULL) { - } +struct ReferenceMap2Visitor : public CheckReferenceMapVisitor { + explicit ReferenceMap2Visitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : CheckReferenceMapVisitor(thread) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* m = GetMethod(); - if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) { + if (CheckReferenceMapVisitor::VisitFrame()) { return true; } - LOG(INFO) << "At " << PrettyMethod(m, false); - - NativePcOffsetToReferenceMap map(m->GetNativeGcMap()); - - if (m->IsCalleeSaveMethod()) { - LOG(WARNING) << "no PC for " << PrettyMethod(m); - return true; - } - - const uint8_t* ref_bitmap = NULL; + mirror::ArtMethod* m = GetMethod(); std::string m_name(m->GetName()); // Given the method name and the number of times the method has been called, // we know the Dex registers with live reference values. Assert that what we // find is what is expected. if (m_name.compare("f") == 0) { - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x03U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8); // v8: this - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x06U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 1); // v8: this, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x08U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0cU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x0eU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x10U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 3, 1); // v8: this, v3: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x13U))); - CHECK(ref_bitmap); + CHECK_REGS_CONTAIN_REFS(0x03U, 8); // v8: this + CHECK_REGS_CONTAIN_REFS(0x06U, 8, 1); // v8: this, v1: x + CHECK_REGS_CONTAIN_REFS(0x08U, 8, 3, 1); // v8: this, v3: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x0cU, 8, 3, 1); // v8: this, v3: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x0eU, 8, 3, 1); // v8: this, v3: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x10U, 8, 3, 1); // v8: this, v3: y, v1: x // v2 is added because of the instruction at DexPC 0024. Object merges with 0 is Object. See: // 0024: move-object v3, v2 // 0025: goto 0013 // Detaled dex instructions for ReferenceMap.java are at the end of this function. // CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1); // v8: this, v3: y, v2: y, v1: x // We eliminate the non-live registers at a return, so only v3 is live: - CHECK_REGS_CONTAIN_REFS(3); // v3: y - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x18U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1aU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1dU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x1fU))); - CHECK(ref_bitmap); + CHECK_REGS_CONTAIN_REFS(0x13U); // v3: y + CHECK_REGS_CONTAIN_REFS(0x18U, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex + CHECK_REGS_CONTAIN_REFS(0x1aU, 8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex + CHECK_REGS_CONTAIN_REFS(0x1dU, 8, 5, 2, 1, 0); // v8: this, v5: x[1], v2: y, v1: x, v0: ex // v5 is removed from the root set because there is a "merge" operation. // See 0015: if-nez v2, 001f. - CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x21U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x27U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x29U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2cU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x2fU))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x - - ref_bitmap = map.FindBitMap(m->NativePcOffset(m->ToNativePc(0x32U))); - CHECK(ref_bitmap); - CHECK_REGS_CONTAIN_REFS(8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex + CHECK_REGS_CONTAIN_REFS(0x1fU, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex + CHECK_REGS_CONTAIN_REFS(0x21U, 8, 2, 1, 0); // v8: this, v2: y, v1: x, v0: ex + CHECK_REGS_CONTAIN_REFS(0x27U, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x29U, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x2cU, 8, 4, 2, 1); // v8: this, v4: ex, v2: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x2fU, 8, 4, 3, 2, 1); // v8: this, v4: ex, v3: y, v2: y, v1: x + CHECK_REGS_CONTAIN_REFS(0x32U, 8, 3, 2, 1, 0); // v8: this, v3: y, v2: y, v1: x, v0: ex } return true; diff --git a/test/004-StackWalk/stack_walk_jni.cc b/test/004-StackWalk/stack_walk_jni.cc index 30a0d5906a..c40de7e247 100644 --- a/test/004-StackWalk/stack_walk_jni.cc +++ b/test/004-StackWalk/stack_walk_jni.cc @@ -14,54 +14,29 @@ * limitations under the License. */ -#include <stdio.h> -#include <memory> - -#include "class_linker.h" -#include "gc_map.h" -#include "mirror/art_method-inl.h" -#include "mirror/class-inl.h" -#include "mirror/object_array-inl.h" -#include "mirror/object-inl.h" +#include "check_reference_map_visitor.h" #include "jni.h" -#include "scoped_thread_state_change.h" namespace art { -#define REG(reg_bitmap, reg) \ - (((reg) < m->GetCodeItem()->registers_size_) && \ - ((*((reg_bitmap) + (reg)/8) >> ((reg) % 8) ) & 0x01)) - -#define CHECK_REGS(...) if (!IsShadowFrame()) { \ - int t[] = {__VA_ARGS__}; \ - int t_size = sizeof(t) / sizeof(*t); \ - for (int i = 0; i < t_size; ++i) \ - CHECK(REG(reg_bitmap, t[i])) << "Error: Reg " << i << " is not in RegisterMap"; \ - } +#define CHECK_REGS(...) do { \ + int t[] = {__VA_ARGS__}; \ + int t_size = sizeof(t) / sizeof(*t); \ + CheckReferences(t, t_size, GetNativePcOffset()); \ +} while (false); static int gJava_StackWalk_refmap_calls = 0; -struct TestReferenceMapVisitor : public StackVisitor { - explicit TestReferenceMapVisitor(Thread* thread) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) - : StackVisitor(thread, NULL) { - } +class TestReferenceMapVisitor : public CheckReferenceMapVisitor { + public: + explicit TestReferenceMapVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) + : CheckReferenceMapVisitor(thread) {} bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - mirror::ArtMethod* m = GetMethod(); - CHECK(m != NULL); - LOG(INFO) << "At " << PrettyMethod(m, false); - - if (m->IsCalleeSaveMethod() || m->IsNative()) { - LOG(WARNING) << "no PC for " << PrettyMethod(m); - CHECK_EQ(GetDexPc(), DexFile::kDexNoIndex); + if (CheckReferenceMapVisitor::VisitFrame()) { return true; } - const uint8_t* reg_bitmap = NULL; - if (!IsShadowFrame()) { - NativePcOffsetToReferenceMap map(m->GetNativeGcMap()); - reg_bitmap = map.FindBitMap(GetNativePcOffset()); - } + mirror::ArtMethod* m = GetMethod(); StringPiece m_name(m->GetName()); // Given the method name and the number of times the method has been called, diff --git a/test/401-optimizing-compiler/src/Main.java b/test/401-optimizing-compiler/src/Main.java index 2c6d1c2b60..07c407b565 100644 --- a/test/401-optimizing-compiler/src/Main.java +++ b/test/401-optimizing-compiler/src/Main.java @@ -97,6 +97,11 @@ public class Main { if (exception == null) { throw new Error("Missing NullPointerException"); } + + result = $opt$InvokeVirtualMethod(); + if (result != 42) { + throw new Error("Unexpected result: " + result); + } } public static void invokePrivate() { @@ -205,5 +210,13 @@ public class Main { m.o = new Main(); } + public static int $opt$InvokeVirtualMethod() { + return new Main().virtualMethod(); + } + + public int virtualMethod() { + return 42; + } + Object o; } |