Merge "Only log an error if an unattached thread is unregistered."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index db60ff8..4ecda91 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -79,6 +79,7 @@
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 b265ee7..9fa5fac 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -1094,6 +1094,7 @@
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 6910e04..d743f90 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -607,7 +607,7 @@
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 @@
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 3c76098..e7bd357 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -260,7 +260,7 @@
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 c0f91d1..eb3de97 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -27,7 +27,8 @@
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 a03588f..33b00d2 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -331,18 +331,61 @@
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 @@
}
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 @@
}
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 9903092..ad62279 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 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 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 660294b..2480960 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -93,6 +93,8 @@
#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 3dd9b37..3383cb2 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 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 @@
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 7c50204..f1be0ad 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -94,6 +94,8 @@
#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 2f352e0..ca03af8 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 @@
}
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 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 44552ea..78b60fe 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -91,6 +91,8 @@
#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 ed6dd93..d6dfeae 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -422,6 +422,7 @@
M(If) \
M(IntConstant) \
M(InvokeStatic) \
+ M(InvokeVirtual) \
M(LoadLocal) \
M(Local) \
M(LongConstant) \
@@ -1272,6 +1273,26 @@
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 75f4155..a539192 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -38,7 +38,7 @@
*/
class CodeVectorAllocator FINAL : public CodeAllocator {
public:
- CodeVectorAllocator() { }
+ CodeVectorAllocator() {}
virtual uint8_t* Allocate(size_t size) {
size_ = size;
@@ -70,6 +70,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 be6f097..afc01dc 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -160,7 +160,14 @@
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 @@
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 @@
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 @@
* 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 41ee213..c97bf64 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 183e692..487f433 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 @@
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 56023c1..54e7761 100644
--- a/disassembler/disassembler_arm.cc
+++ b/disassembler/disassembler_arm.cc
@@ -94,7 +94,7 @@
}
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 @@
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 @@
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 @@
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 f6d7fda..f870e8e 100644
--- a/disassembler/disassembler_arm.h
+++ b/disassembler/disassembler_arm.h
@@ -26,8 +26,7 @@
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 864d22d..5d0c218 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -34,7 +34,8 @@
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 28c0fa7..ad20c70 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -27,7 +27,7 @@
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 5e89f6f..bd5fac7 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -168,7 +168,7 @@
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 @@
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 @@
}
}
- 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 e1fb034..00b2f8d 100644
--- a/disassembler/disassembler_mips.h
+++ b/disassembler/disassembler_mips.h
@@ -26,8 +26,7 @@
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 1848abe..1d29765 100644
--- a/disassembler/disassembler_x86.cc
+++ b/disassembler/disassembler_x86.cc
@@ -1215,7 +1215,9 @@
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 @@
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 2565bb1..f448662 100644
--- a/disassembler/disassembler_x86.h
+++ b/disassembler/disassembler_x86.h
@@ -24,8 +24,8 @@
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 1cf7757..bac3c33 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -79,8 +79,8 @@
" 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 @@
" 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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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";
-
- *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());
-
- if (oat_method.GetNativeGcMap() != nullptr) {
- // The native GC map is null for methods compiled with the optimizing compiler.
- DumpVmap(*indent2_os, oat_method);
+ *indent1_os << "OatMethodOffsets ";
+ if (options_->absolute_addresses_) {
+ *indent1_os << StringPrintf("%p ", oat_method_offsets);
}
- 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_) {
- Indenter indent3_filter(indent2_os->rdbuf(), kIndentChar, kIndentBy1Count);
- std::ostream indent3_os(&indent3_filter);
- DumpMappingTable(indent3_os, oat_method);
+ *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("gc_map: %p (offset=0x%08x)\n",
- oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
- if (dump_raw_gc_map_) {
+
+ 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";
+
+ *indent2_os << "gc_map: ";
+ if (options_->absolute_addresses_) {
+ *indent2_os << StringPrintf("%p ", oat_method.GetNativeGcMap());
+ }
+ 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);
DumpGcMap(indent3_os, oat_method, code_item);
}
}
{
- 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 << "OatQuickMethodHeader ";
+ uint32_t method_header_offset = oat_method.GetOatQuickMethodHeaderOffset();
+ const OatQuickMethodHeader* method_header = oat_method.GetOatQuickMethodHeader();
- DumpCode(*indent2_os, verifier.get(), oat_method, code_item);
+ if (options_->absolute_addresses_) {
+ *indent1_os << StringPrintf("%p ", method_header);
+ }
+ *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);
+ 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);
+ }
+ }
}
+ {
+ *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;
+
+ 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 @@
}
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 @@
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 @@
}
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 @@
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 @@
os << std::flush;
- oat_dumper_->Dump(os);
+ return oat_dumper_->Dump(os);
}
private:
@@ -1727,12 +1946,11 @@
// 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 @@
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 @@
} 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 @@
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 @@
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 @@
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 a7adb02..2c3e966 100644
--- a/runtime/base/allocator.h
+++ b/runtime/base/allocator.h
@@ -66,6 +66,7 @@
kAllocatorTagCompileTimeClassPath,
kAllocatorTagOatFile,
kAllocatorTagDexFileVerifier,
+ kAllocatorTagRosAlloc,
kAllocatorTagCount, // Must always be last element.
};
std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag);
@@ -149,6 +150,10 @@
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 455680b..2c95ede 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -884,6 +884,10 @@
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 @@
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 20f58de..8d2cdce 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -92,12 +92,12 @@
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 0000000..0bfde33
--- /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 0000000..1a78d72
--- /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 cc1e0b9..6c37402 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1886,11 +1886,25 @@
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::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 @@
return error;
}
-std::string Dbg::GetThreadGroupName(JDWP::ObjectId thread_group_id) {
- ScopedObjectAccess soa(Thread::Current());
- JDWP::JdwpError error;
- mirror::Object* thread_group = gRegistry->Get<mirror::Object*>(thread_group_id, &error);
- CHECK(thread_group != nullptr) << error;
- const char* old_cause = soa.Self()->StartAssertNoThreadSuspension("Debugger: GetThreadGroupName");
+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 = 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);
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,7 +2039,64 @@
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;
+}
+
+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());
+ 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::GetSystemThreadGroupId() {
@@ -2007,13 +2106,6 @@
return gRegistry->Add(group);
}
-JDWP::ObjectId Dbg::GetMainThreadGroupId() {
- ScopedObjectAccess soa(Thread::Current());
- mirror::ArtField* f = soa.DecodeField(WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup);
- mirror::Object* group = f->GetObject(f->GetDeclaringClass());
- return gRegistry->Add(group);
-}
-
JDWP::JdwpThreadStatus Dbg::ToJdwpThreadStatus(ThreadState state) {
switch (state) {
case kBlocked:
@@ -2111,11 +2203,8 @@
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::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 @@
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 @@
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 219210e..e171d78 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -381,7 +381,7 @@
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 @@
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 @@
// 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 64c9185..6a9976a 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 ad22a2e..a7e5e74 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -569,7 +569,7 @@
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 @@
}
// 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 @@
// 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 @@
}
// 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 @@
// 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 @@
} 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 b2a5a3c..2fbd97a 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 @@
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 @@
// 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 @@
// 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 @@
// 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 4044852..b3bed64 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -547,8 +547,11 @@
}
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 95530be..930499a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -374,7 +374,8 @@
}
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 @@
// 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 @@
}
// 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::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 8fb33ce..c8fa869 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -365,23 +365,23 @@
}
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::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 @@
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 @@
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 @@
// 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 5a58446..4ed6abc 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 @@
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 @@
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 bb7da0d..18441c1 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -83,13 +83,6 @@
// 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 @@
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 @@
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 @@
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;
}
- CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
- AddSpace(large_object_space_);
+ if (large_object_space_ != nullptr) {
+ 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 @@
}
}
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 @@
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 @@
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 @@
} 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 351e1c6..faaea40 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 @@
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 @@
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 @@
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 2a43712..dad5855 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -120,7 +120,7 @@
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 @@
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 @@
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 9d5e090..a63c5c0 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,6 +31,12 @@
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 0f45b9e..a2e88a6 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -597,10 +597,13 @@
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 @@
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 d05cee5..3c1c756 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -182,10 +182,10 @@
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 @@
// 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 35095f9..e0a83f6 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -151,7 +151,12 @@
/* 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_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 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 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_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_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 ae17070..8447616 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -285,14 +285,17 @@
}
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 d37aa57..de6ec05 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -155,7 +155,9 @@
// 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 @@
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 ceff206..d8a537f 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -60,11 +60,11 @@
}
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 97ca6b2..9570bb5 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 @@
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 c621e88..a896f3e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -454,8 +454,12 @@
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 @@
}
}
-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 2fd4f4c..b9d5702 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -114,13 +114,22 @@
}
}
+ // 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 @@
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 @@
}
// 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 @@
// 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 37e08a5..6b4f764 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -63,6 +63,8 @@
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 @@
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 @@
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 @@
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 @@
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 3839e19..26a2f31 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 @@
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 a1ea3cf..0e382ff 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -697,6 +697,8 @@
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::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 cfb1abc..f9c017b 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -390,7 +390,7 @@
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 8e5da35..44e36c4 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -604,8 +604,8 @@
* | 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 c54bebe..650b0f9 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -961,7 +961,7 @@
// 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 6dcc5fe..b32e042 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -373,11 +373,9 @@
// 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 @@
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 f241281..f28d488 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -311,9 +311,15 @@
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::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 @@
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::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 7929554..e914bd9 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(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);
-#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)
-
-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_) {
+ if (CheckReferenceMapVisitor::VisitFrame()) {
+ return true;
+ }
mirror::ArtMethod* m = GetMethod();
- if (!m || m->IsNative() || m->IsRuntimeMethod() || IsShadowFrame()) {
- 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;
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 30a0d59..c40de7e 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 2c6d1c2..07c407b 100644
--- a/test/401-optimizing-compiler/src/Main.java
+++ b/test/401-optimizing-compiler/src/Main.java
@@ -97,6 +97,11 @@
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 @@
m.o = new Main();
}
+ public static int $opt$InvokeVirtualMethod() {
+ return new Main().virtualMethod();
+ }
+
+ public int virtualMethod() {
+ return 42;
+ }
+
Object o;
}