Merge "ART: Fix loading PIC oat files without image for oatdump."
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 10b3fe1..1218586 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -669,10 +669,10 @@
#endif
#ifdef ART_ENABLE_CODEGEN_mips
case kMips: {
- mips::PcRelativeFixups* pc_relative_fixups =
- new (arena) mips::PcRelativeFixups(graph, codegen, stats);
SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
+ mips::PcRelativeFixups* pc_relative_fixups =
+ new (arena) mips::PcRelativeFixups(graph, codegen, stats);
HOptimization* mips_optimizations[] = {
side_effects,
gvn,
@@ -696,11 +696,15 @@
#endif
#ifdef ART_ENABLE_CODEGEN_x86
case kX86: {
+ SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+ GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
x86::PcRelativeFixups* pc_relative_fixups =
new (arena) x86::PcRelativeFixups(graph, codegen, stats);
x86::X86MemoryOperandGeneration* memory_gen =
new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
HOptimization* x86_optimizations[] = {
+ side_effects,
+ gvn,
pc_relative_fixups,
memory_gen
};
@@ -710,9 +714,13 @@
#endif
#ifdef ART_ENABLE_CODEGEN_x86_64
case kX86_64: {
+ SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph);
+ GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN$after_arch");
x86::X86MemoryOperandGeneration* memory_gen =
new (arena) x86::X86MemoryOperandGeneration(graph, codegen, stats);
HOptimization* x86_64_optimizations[] = {
+ side_effects,
+ gvn,
memory_gen
};
RunOptimizations(x86_64_optimizations, arraysize(x86_64_optimizations), pass_observer);
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 7f841fc..f05977a 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -86,20 +86,38 @@
TiMethodCallback gMethodCallback;
+// TODO We should make this much more selective in the future so we only return true when we
+// actually care about the method (i.e. had locals changed, have breakpoints, etc.). For now though
+// we can just assume that we care we are loaded at all.
+//
+// Even if we don't keep track of this at the method level we might want to keep track of it at the
+// level of enabled capabilities.
+struct TiMethodInspectionCallback : public art::MethodInspectionCallback {
+ bool IsMethodBeingInspected(art::ArtMethod* method ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ return true;
+ }
+};
+
+TiMethodInspectionCallback gMethodInspectionCallback;
+
void MethodUtil::Register(EventHandler* handler) {
gMethodCallback.event_handler = handler;
art::ScopedThreadStateChange stsc(art::Thread::Current(),
art::ThreadState::kWaitingForDebuggerToAttach);
art::ScopedSuspendAll ssa("Add method callback");
- art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback);
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->AddMethodCallback(&gMethodCallback);
+ callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
}
void MethodUtil::Unregister() {
art::ScopedThreadStateChange stsc(art::Thread::Current(),
art::ThreadState::kWaitingForDebuggerToAttach);
art::ScopedSuspendAll ssa("Remove method callback");
- art::Runtime* runtime = art::Runtime::Current();
- runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback);
+ art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
+ callbacks->RemoveMethodCallback(&gMethodCallback);
+ callbacks->AddMethodInspectionCallback(&gMethodInspectionCallback);
}
jvmtiError MethodUtil::GetBytecodes(jvmtiEnv* env,
diff --git a/runtime/Android.bp b/runtime/Android.bp
index a3a45e1..711bc65 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -387,7 +387,7 @@
],
shared_libs: [
"libziparchive",
- "libz-host",
+ "libz",
],
},
},
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 6daec72..b021ff1 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -324,6 +324,7 @@
bool Dbg::gDebuggerActive = false;
bool Dbg::gDisposed = false;
ObjectRegistry* Dbg::gRegistry = nullptr;
+DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback;
// Deoptimization support.
std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
@@ -341,6 +342,10 @@
Dbg::DbgThreadLifecycleCallback Dbg::thread_lifecycle_callback_;
Dbg::DbgClassLoadCallback Dbg::class_load_callback_;
+bool DebuggerActiveMethodInspectionCallback::IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED) {
+ return Dbg::IsDebuggerActive();
+}
+
// Breakpoints.
static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
@@ -652,6 +657,7 @@
}
instrumentation_events_ = 0;
gDebuggerActive = true;
+ Runtime::Current()->GetRuntimeCallbacks()->AddMethodInspectionCallback(&gDebugActiveCallback);
LOG(INFO) << "Debugger is active";
}
@@ -689,6 +695,8 @@
runtime->GetInstrumentation()->DisableDeoptimization(kDbgInstrumentationKey);
}
gDebuggerActive = false;
+ Runtime::Current()->GetRuntimeCallbacks()->RemoveMethodInspectionCallback(
+ &gDebugActiveCallback);
}
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 0be46d6..18126b1 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -34,6 +34,7 @@
#include "jni.h"
#include "jvalue.h"
#include "obj_ptr.h"
+#include "runtime_callbacks.h"
#include "thread.h"
#include "thread_state.h"
@@ -51,6 +52,12 @@
class StackVisitor;
class Thread;
+struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
+ bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED)
+ OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+
/*
* Invoke-during-breakpoint support.
*/
@@ -773,6 +780,8 @@
// Indicates whether the debugger is making requests.
static bool gDebuggerActive;
+ static DebuggerActiveMethodInspectionCallback gDebugActiveCallback;
+
// Indicates whether we should drop the JDWP connection because the runtime stops or the
// debugger called VirtualMachine.Dispose.
static bool gDisposed;
diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h
index 6585875..280746e 100644
--- a/runtime/dex_instruction_iterator.h
+++ b/runtime/dex_instruction_iterator.h
@@ -26,8 +26,8 @@
class DexInstructionIterator : public std::iterator<std::forward_iterator_tag, Instruction> {
public:
- using Type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
- using difference_type = typename std::iterator<std::forward_iterator_tag, Type>::difference_type;
+ using value_type = std::iterator<std::forward_iterator_tag, Instruction>::value_type;
+ using difference_type = std::iterator<std::forward_iterator_tag, value_type>::difference_type;
DexInstructionIterator() = default;
DexInstructionIterator(const DexInstructionIterator&) = default;
@@ -35,12 +35,8 @@
DexInstructionIterator& operator=(const DexInstructionIterator&) = default;
DexInstructionIterator& operator=(DexInstructionIterator&&) = default;
- explicit DexInstructionIterator(const Type* inst) : inst_(inst) {}
- explicit DexInstructionIterator(const uint16_t* inst) : inst_(Type::At(inst)) {}
-
- ALWAYS_INLINE bool operator==(const DexInstructionIterator& other) const {
- return inst_ == other.inst_ || inst_ == nullptr || other.inst_ == nullptr;
- }
+ explicit DexInstructionIterator(const value_type* inst) : inst_(inst) {}
+ explicit DexInstructionIterator(const uint16_t* inst) : inst_(value_type::At(inst)) {}
// Value after modification.
DexInstructionIterator& operator++() {
@@ -55,11 +51,11 @@
return temp;
}
- const Type& operator*() const {
+ const value_type& operator*() const {
return *inst_;
}
- const Type* operator->() const {
+ const value_type* operator->() const {
return &**this;
}
@@ -69,14 +65,19 @@
reinterpret_cast<const uint16_t*>(code_item_begin.inst_);
}
- const Type* Inst() const {
+ const value_type* Inst() const {
return inst_;
}
private:
- const Type* inst_ = nullptr;
+ const value_type* inst_ = nullptr;
};
+static ALWAYS_INLINE inline bool operator==(const DexInstructionIterator& lhs,
+ const DexInstructionIterator& rhs) {
+ return lhs.Inst() == rhs.Inst();
+}
+
static inline bool operator!=(const DexInstructionIterator& lhs,
const DexInstructionIterator& rhs) {
return !(lhs == rhs);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index eef2773..813a264 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1119,8 +1119,8 @@
const DexFile::CodeItem* code;
code = caller->GetCodeItem();
CHECK_LT(dex_pc, code->insns_size_in_code_units_);
- const Instruction* instr = &code->InstructionAt(dex_pc);
- Instruction::Code instr_code = instr->Opcode();
+ const Instruction& instr = code->InstructionAt(dex_pc);
+ Instruction::Code instr_code = instr.Opcode();
bool is_range;
switch (instr_code) {
case Instruction::INVOKE_DIRECT:
@@ -1164,10 +1164,10 @@
is_range = true;
break;
default:
- LOG(FATAL) << "Unexpected call into trampoline: " << instr->DumpString(nullptr);
+ LOG(FATAL) << "Unexpected call into trampoline: " << instr.DumpString(nullptr);
UNREACHABLE();
}
- called_method.index = (is_range) ? instr->VRegB_3rc() : instr->VRegB_35c();
+ called_method.index = (is_range) ? instr.VRegB_3rc() : instr.VRegB_35c();
// Check that the invoke matches what we expected, note that this path only happens for debug
// builds.
if (found_stack_map) {
@@ -2484,16 +2484,16 @@
uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
- const Instruction* instr = &code_item->InstructionAt(dex_pc);
- Instruction::Code instr_code = instr->Opcode();
+ const Instruction& instr = code_item->InstructionAt(dex_pc);
+ Instruction::Code instr_code = instr.Opcode();
DCHECK(instr_code == Instruction::INVOKE_INTERFACE ||
instr_code == Instruction::INVOKE_INTERFACE_RANGE)
- << "Unexpected call into interface trampoline: " << instr->DumpString(nullptr);
+ << "Unexpected call into interface trampoline: " << instr.DumpString(nullptr);
if (instr_code == Instruction::INVOKE_INTERFACE) {
- dex_method_idx = instr->VRegB_35c();
+ dex_method_idx = instr.VRegB_35c();
} else {
DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
- dex_method_idx = instr->VRegB_3rc();
+ dex_method_idx = instr.VRegB_3rc();
}
const DexFile& dex_file = caller_method->GetDeclaringClass()->GetDexFile();
@@ -2600,11 +2600,11 @@
ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp);
uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
const DexFile::CodeItem* code = caller_method->GetCodeItem();
- const Instruction* inst = &code->InstructionAt(dex_pc);
- DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC ||
- inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+ const Instruction& inst = code->InstructionAt(dex_pc);
+ DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC ||
+ inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
const DexFile* dex_file = caller_method->GetDexFile();
- const uint32_t proto_idx = inst->VRegH();
+ const uint32_t proto_idx = inst.VRegH();
const char* shorty = dex_file->GetShorty(proto_idx);
const size_t shorty_length = strlen(shorty);
static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static.
@@ -2621,7 +2621,7 @@
// Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact().
ClassLinker* linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
- self, inst->VRegB(), caller_method, kVirtual);
+ self, inst.VRegB(), caller_method, kVirtual);
DCHECK((resolved_method ==
jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) ||
(resolved_method ==
@@ -2642,15 +2642,15 @@
return static_cast<uintptr_t>('V');
}
- DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst->VRegA());
+ DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA());
DCHECK_EQ(resolved_method->IsStatic(), kMethodIsStatic);
// Fix references before constructing the shadow frame.
gc_visitor.FixupReferences();
// Construct shadow frame placing arguments consecutively from |first_arg|.
- const bool is_range = (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
- const size_t num_vregs = is_range ? inst->VRegA_4rcc() : inst->VRegA_45cc();
+ const bool is_range = (inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+ const size_t num_vregs = is_range ? inst.VRegA_4rcc() : inst.VRegA_45cc();
const size_t first_arg = 0;
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
CREATE_SHADOW_FRAME(num_vregs, /* link */ nullptr, resolved_method, dex_pc);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 8c27bfe..97a3b71 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -490,10 +490,10 @@
return false;
}
- // Before allowing the jump, make sure the debugger is not active to avoid jumping from
- // interpreter to OSR while e.g. single stepping. Note that we could selectively disable
- // OSR when single stepping, but that's currently hard to know at this point.
- if (Dbg::IsDebuggerActive()) {
+ // Before allowing the jump, make sure no code is actively inspecting the method to avoid
+ // jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
+ // disable OSR when single stepping, but that's currently hard to know at this point.
+ if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
return false;
}
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index d8b6237..f94923e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -166,10 +166,9 @@
<< line_number << ")";
}
}
- if (clear_exception_) {
- // Exception was cleared as part of delivery.
- DCHECK(!self_->IsExceptionPending());
- } else {
+ // Exception was cleared as part of delivery.
+ DCHECK(!self_->IsExceptionPending());
+ if (!clear_exception_) {
// Put exception back in root set with clear throw location.
self_->SetException(exception_ref.Get());
}
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 8090f9b..12b63c9 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -112,6 +112,10 @@
handler_dex_pc_ = dex_pc;
}
+ bool GetClearException() const {
+ return clear_exception_;
+ }
+
void SetClearException(bool clear_exception) {
clear_exception_ = clear_exception;
}
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 88d3f28..f164f7c 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -26,10 +26,6 @@
namespace art {
-void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
- thread_callbacks_.push_back(cb);
-}
-
template <typename T>
ALWAYS_INLINE
static inline void Remove(T* cb, std::vector<T*>* data) {
@@ -39,6 +35,27 @@
}
}
+void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) {
+ method_inspection_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMethodInspectionCallback(MethodInspectionCallback* cb) {
+ Remove(cb, &method_inspection_callbacks_);
+}
+
+bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
+ for (MethodInspectionCallback* cb : method_inspection_callbacks_) {
+ if (cb->IsMethodBeingInspected(m)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void RuntimeCallbacks::AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
+ thread_callbacks_.push_back(cb);
+}
+
void RuntimeCallbacks::MonitorContendedLocking(Monitor* m) {
for (MonitorCallback* cb : monitor_callbacks_) {
cb->MonitorContendedLocking(m);
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index fa686d3..c936049 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -94,6 +94,18 @@
virtual ~MonitorCallback() {}
};
+// A callback to let parts of the runtime note that they are currently relying on a particular
+// method remaining in it's current state. Users should not rely on always being called. If multiple
+// callbacks are added the runtime will short-circuit when the first one returns 'true'.
+class MethodInspectionCallback {
+ public:
+ virtual ~MethodInspectionCallback() {}
+
+ // Returns true if the method is being inspected currently and the runtime should not modify it in
+ // potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
+ virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
class RuntimeCallbacks {
public:
void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -151,6 +163,15 @@
void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
+ // on by some code.
+ bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void AddMethodInspectionCallback(MethodInspectionCallback* cb)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveMethodInspectionCallback(MethodInspectionCallback* cb)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
std::vector<ThreadLifecycleCallback*> thread_callbacks_
GUARDED_BY(Locks::mutator_lock_);
@@ -164,6 +185,8 @@
GUARDED_BY(Locks::mutator_lock_);
std::vector<MonitorCallback*> monitor_callbacks_
GUARDED_BY(Locks::mutator_lock_);
+ std::vector<MethodInspectionCallback*> method_inspection_callbacks_
+ GUARDED_BY(Locks::mutator_lock_);
};
} // namespace art
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 968a23b..2412931 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3080,6 +3080,10 @@
UNREACHABLE();
}
+ if (kUseReadBarrier) {
+ ReadBarrier::AssertToSpaceInvariant(exception.Ptr());
+ }
+
// This is a real exception: let the instrumentation know about it.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (instrumentation->HasExceptionThrownListeners() &&
@@ -3121,6 +3125,18 @@
QuickExceptionHandler exception_handler(this, false);
exception_handler.FindCatch(exception);
exception_handler.UpdateInstrumentationStack();
+ if (exception_handler.GetClearException()) {
+ // Exception was cleared as part of delivery.
+ DCHECK(!IsExceptionPending());
+ } else {
+ // Exception was put back with a throw location.
+ DCHECK(IsExceptionPending());
+ if (kUseReadBarrier) {
+ // Check the to-space invariant on the re-installed exception.
+ ObjPtr<mirror::Throwable> reinstalled_exception = GetException();
+ ReadBarrier::AssertToSpaceInvariant(reinstalled_exception.Ptr());
+ }
+ }
exception_handler.DoLongJump();
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index b789ac6..cfdf20d 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1955,7 +1955,7 @@
if (dead_start >= 0) {
LogVerifyInfo()
<< "dead code " << reinterpret_cast<void*>(dead_start)
- << "-" << reinterpret_cast<void*>(instructions.end().GetDexPC(instructions.begin()));
+ << "-" << reinterpret_cast<void*>(instructions.end().GetDexPC(instructions.begin()) - 1);
}
// To dump the state of the verify after a method, do something like:
// if (dex_file_->PrettyMethod(dex_method_idx_) ==
@@ -2637,7 +2637,7 @@
break;
}
- const Instruction* instance_of_inst = &code_item_->InstructionAt(instance_of_idx);
+ const Instruction& instance_of_inst = code_item_->InstructionAt(instance_of_idx);
/* Check for peep-hole pattern of:
* ...;
@@ -2652,9 +2652,9 @@
* - when vX == vY.
*/
if (!CurrentInsnFlags()->IsBranchTarget() &&
- (Instruction::INSTANCE_OF == instance_of_inst->Opcode()) &&
- (inst->VRegA_21t() == instance_of_inst->VRegA_22c()) &&
- (instance_of_inst->VRegA_22c() != instance_of_inst->VRegB_22c())) {
+ (Instruction::INSTANCE_OF == instance_of_inst.Opcode()) &&
+ (inst->VRegA_21t() == instance_of_inst.VRegA_22c()) &&
+ (instance_of_inst.VRegA_22c() != instance_of_inst.VRegB_22c())) {
// Check the type of the instance-of is different than that of registers type, as if they
// are the same there is no work to be done here. Check that the conversion is not to or
// from an unresolved type as type information is imprecise. If the instance-of is to an
@@ -2665,9 +2665,9 @@
// type is assignable to the original then allow optimization. This check is performed to
// ensure that subsequent merges don't lose type information - such as becoming an
// interface from a class that would lose information relevant to field checks.
- const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst->VRegB_22c());
+ const RegType& orig_type = work_line_->GetRegisterType(this, instance_of_inst.VRegB_22c());
const RegType& cast_type = ResolveClass<CheckAccess::kYes>(
- dex::TypeIndex(instance_of_inst->VRegC_22c()));
+ dex::TypeIndex(instance_of_inst.VRegC_22c()));
if (!orig_type.Equals(cast_type) &&
!cast_type.IsUnresolvedTypes() && !orig_type.IsUnresolvedTypes() &&
@@ -2684,7 +2684,7 @@
}
update_line->CopyFromLine(work_line_.get());
update_line->SetRegisterType<LockOp::kKeep>(this,
- instance_of_inst->VRegB_22c(),
+ instance_of_inst.VRegB_22c(),
cast_type);
if (!GetInstructionFlags(instance_of_idx).IsBranchTarget() && 0 != instance_of_idx) {
// See if instance-of was preceded by a move-object operation, common due to the small
@@ -2699,26 +2699,26 @@
work_insn_idx_)) {
break;
}
- const Instruction* move_inst = &code_item_->InstructionAt(move_idx);
- switch (move_inst->Opcode()) {
+ const Instruction& move_inst = code_item_->InstructionAt(move_idx);
+ switch (move_inst.Opcode()) {
case Instruction::MOVE_OBJECT:
- if (move_inst->VRegA_12x() == instance_of_inst->VRegB_22c()) {
+ if (move_inst.VRegA_12x() == instance_of_inst.VRegB_22c()) {
update_line->SetRegisterType<LockOp::kKeep>(this,
- move_inst->VRegB_12x(),
+ move_inst.VRegB_12x(),
cast_type);
}
break;
case Instruction::MOVE_OBJECT_FROM16:
- if (move_inst->VRegA_22x() == instance_of_inst->VRegB_22c()) {
+ if (move_inst.VRegA_22x() == instance_of_inst.VRegB_22c()) {
update_line->SetRegisterType<LockOp::kKeep>(this,
- move_inst->VRegB_22x(),
+ move_inst.VRegB_22x(),
cast_type);
}
break;
case Instruction::MOVE_OBJECT_16:
- if (move_inst->VRegA_32x() == instance_of_inst->VRegB_22c()) {
+ if (move_inst.VRegA_32x() == instance_of_inst.VRegB_22c()) {
update_line->SetRegisterType<LockOp::kKeep>(this,
- move_inst->VRegB_32x(),
+ move_inst.VRegB_32x(),
cast_type);
}
break;
diff --git a/test/1935-get-set-current-frame-jit/expected.txt b/test/1935-get-set-current-frame-jit/expected.txt
new file mode 100644
index 0000000..fed993c
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/expected.txt
@@ -0,0 +1,7 @@
+JNI_OnLoad called
+From GetLocalInt(), value is 42
+isInterpreted? true
+ Value is '42'
+Setting TARGET to 1337
+isInterpreted? true
+ Value is '1337'
diff --git a/test/1935-get-set-current-frame-jit/info.txt b/test/1935-get-set-current-frame-jit/info.txt
new file mode 100644
index 0000000..7342af7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/info.txt
@@ -0,0 +1,2 @@
+Tests for jvmti get/set Local variable in the currently executing method frame.
+
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/1935-get-set-current-frame-jit/src/Main.java b/test/1935-get-set-current-frame-jit/src/Main.java
new file mode 100644
index 0000000..eb0a637
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/Main.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import art.Locals;
+import art.StackTrace;
+import art.Suspension;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Semaphore;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.Consumer;
+
+public class Main {
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Locals.EnableLocalVariableAccess();
+ runGet();
+ runSet();
+ }
+
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
+
+ public static class IntRunner implements Runnable {
+ private volatile boolean continueBusyLoop;
+ private volatile boolean inBusyLoop;
+ public IntRunner() {
+ this.continueBusyLoop = true;
+ this.inBusyLoop = false;
+ }
+ public void run() {
+ int TARGET = 42;
+ // We will suspend the thread during this loop.
+ while (continueBusyLoop) {
+ inBusyLoop = true;
+ }
+ int i = 0;
+ while (Main.isInterpreted() && i < 10000) {
+ Main.ensureJitCompiled(IntRunner.class, "run");
+ i++;
+ }
+ // We shouldn't be doing OSR since we are using JVMTI and the get/set local will push us to
+ // interpreter.
+ System.out.println("isInterpreted? " + Main.isInterpreted());
+ reportValue(TARGET);
+ }
+ public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+ public void finish() {
+ continueBusyLoop = false;
+ }
+ }
+
+ public static void runGet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Get Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+ target_get.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_get);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_get.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ } finally {
+ Suspension.resume(target_get);
+ int_runner.finish();
+ target_get.join();
+ }
+ }
+
+ public static void runSet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Set Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+ target_set.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_set);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_set.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ System.out.println("Setting TARGET to " + SET_VALUE);
+ Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+ } finally {
+ Suspension.resume(target_set);
+ int_runner.finish();
+ target_set.join();
+ }
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
+ }
+
+ public static native void ensureJitCompiled(Class k, String f);
+ public static native boolean isInterpreted();
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
new file mode 100644
index 0000000..bbb89f7
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Locals.java b/test/1935-get-set-current-frame-jit/src/art/Locals.java
new file mode 100644
index 0000000..22e21be
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Locals.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.Objects;
+
+public class Locals {
+ public static native void EnableLocalVariableAccess();
+
+ public static class VariableDescription {
+ public final long start_location;
+ public final int length;
+ public final String name;
+ public final String signature;
+ public final String generic_signature;
+ public final int slot;
+
+ public VariableDescription(
+ long start, int length, String name, String sig, String gen_sig, int slot) {
+ this.start_location = start;
+ this.length = length;
+ this.name = name;
+ this.signature = sig;
+ this.generic_signature = gen_sig;
+ this.slot = slot;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "VariableDescription { " +
+ "Sig: '%s', Name: '%s', Gen_sig: '%s', slot: %d, start: %d, len: %d" +
+ "}",
+ this.signature,
+ this.name,
+ this.generic_signature,
+ this.slot,
+ this.start_location,
+ this.length);
+ }
+ public boolean equals(Object other) {
+ if (!(other instanceof VariableDescription)) {
+ return false;
+ } else {
+ VariableDescription v = (VariableDescription)other;
+ return Objects.equals(v.signature, signature) &&
+ Objects.equals(v.name, name) &&
+ Objects.equals(v.generic_signature, generic_signature) &&
+ v.slot == slot &&
+ v.start_location == start_location &&
+ v.length == length;
+ }
+ }
+ public int hashCode() {
+ return Objects.hash(this.signature, this.name, this.generic_signature, this.slot,
+ this.start_location, this.length);
+ }
+ }
+
+ public static native VariableDescription[] GetLocalVariableTable(Executable e);
+
+ public static VariableDescription GetVariableAtLine(
+ Executable e, String name, String sig, int line) throws Exception {
+ return GetVariableAtLocation(e, name, sig, Breakpoint.lineToLocation(e, line));
+ }
+
+ public static VariableDescription GetVariableAtLocation(
+ Executable e, String name, String sig, long loc) {
+ VariableDescription[] vars = GetLocalVariableTable(e);
+ for (VariableDescription var : vars) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(name) &&
+ var.signature.equals(sig)) {
+ return var;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + name + " (sig: " + sig + ") in " + e + " at loc " + loc);
+ }
+
+ public static native int GetLocalVariableInt(Thread thr, int depth, int slot);
+ public static native long GetLocalVariableLong(Thread thr, int depth, int slot);
+ public static native float GetLocalVariableFloat(Thread thr, int depth, int slot);
+ public static native double GetLocalVariableDouble(Thread thr, int depth, int slot);
+ public static native Object GetLocalVariableObject(Thread thr, int depth, int slot);
+ public static native Object GetLocalInstance(Thread thr, int depth);
+
+ public static void SetLocalVariableInt(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableInt(thr, depth, slot, ((Number)val).intValue());
+ }
+ public static void SetLocalVariableLong(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableLong(thr, depth, slot, ((Number)val).longValue());
+ }
+ public static void SetLocalVariableFloat(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableFloat(thr, depth, slot, ((Number)val).floatValue());
+ }
+ public static void SetLocalVariableDouble(Thread thr, int depth, int slot, Object val) {
+ SetLocalVariableDouble(thr, depth, slot, ((Number)val).doubleValue());
+ }
+ public static native void SetLocalVariableInt(Thread thr, int depth, int slot, int val);
+ public static native void SetLocalVariableLong(Thread thr, int depth, int slot, long val);
+ public static native void SetLocalVariableFloat(Thread thr, int depth, int slot, float val);
+ public static native void SetLocalVariableDouble(Thread thr, int depth, int slot, double val);
+ public static native void SetLocalVariableObject(Thread thr, int depth, int slot, Object val);
+}
diff --git a/test/1935-get-set-current-frame-jit/src/art/StackTrace.java b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
new file mode 100644
index 0000000..2ea2f20
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1935-get-set-current-frame-jit/src/art/Suspension.java b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
new file mode 100644
index 0000000..16e62cc
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/Android.bp b/test/Android.bp
index d2eccb0..2af03e3 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -67,6 +67,7 @@
"libicuuc",
"libicui18n",
"libnativehelper",
+ "libz",
],
whole_static_libs: [
"libsigchain",
@@ -84,7 +85,6 @@
],
shared_libs: [
"libziparchive",
- "libz-host",
],
cflags: [
// gtest issue
@@ -103,7 +103,6 @@
],
shared_libs: [
"liblog",
- "libz",
],
cflags: [
// gtest issue
diff --git a/tools/ahat/src/Main.java b/tools/ahat/src/Main.java
index 623a865..31c485d 100644
--- a/tools/ahat/src/Main.java
+++ b/tools/ahat/src/Main.java
@@ -18,6 +18,7 @@
import com.android.ahat.heapdump.AhatSnapshot;
import com.android.ahat.heapdump.Diff;
+import com.android.ahat.heapdump.HprofFormatException;
import com.android.ahat.heapdump.Parser;
import com.android.ahat.proguard.ProguardMap;
import com.sun.net.httpserver.HttpServer;
@@ -47,7 +48,7 @@
out.println("");
}
- public static void main(String[] args) throws Exception {
+ public static void main(String[] args) {
int port = 7100;
for (String arg : args) {
if (arg.equals("--help")) {
@@ -104,34 +105,40 @@
return;
}
- // Launch the server before parsing the hprof file so we get
- // BindExceptions quickly.
- InetAddress loopback = InetAddress.getLoopbackAddress();
- InetSocketAddress addr = new InetSocketAddress(loopback, port);
- HttpServer server = HttpServer.create(addr, 0);
+ try {
+ // Launch the server before parsing the hprof file so we get
+ // BindExceptions quickly.
+ InetAddress loopback = InetAddress.getLoopbackAddress();
+ InetSocketAddress addr = new InetSocketAddress(loopback, port);
+ System.out.println("Preparing " + addr + " ...");
+ HttpServer server = HttpServer.create(addr, 0);
- System.out.println("Processing hprof file...");
- AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
+ System.out.println("Processing '" + hprof + "' ...");
+ AhatSnapshot ahat = Parser.parseHeapDump(hprof, map);
- if (hprofbase != null) {
- System.out.println("Processing baseline hprof file...");
- AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
+ if (hprofbase != null) {
+ System.out.println("Processing '" + hprofbase + "' ...");
+ AhatSnapshot base = Parser.parseHeapDump(hprofbase, mapbase);
- System.out.println("Diffing hprof files...");
- Diff.snapshots(ahat, base);
+ System.out.println("Diffing heap dumps ...");
+ Diff.snapshots(ahat, base);
+ }
+
+ server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
+ server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
+ server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
+ server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
+ server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
+ server.createContext("/bitmap", new BitmapHandler(ahat));
+ server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
+ server.setExecutor(Executors.newFixedThreadPool(1));
+ System.out.println("Server started on localhost:" + port);
+
+ server.start();
+ } catch (HprofFormatException|IOException e) {
+ System.err.println("Unable to launch ahat:");
+ e.printStackTrace();
}
-
- server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase)));
- server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat)));
- server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat)));
- server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat)));
- server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat)));
- server.createContext("/bitmap", new BitmapHandler(ahat));
- server.createContext("/style.css", new StaticHandler("style.css", "text/css"));
- server.setExecutor(Executors.newFixedThreadPool(1));
- System.out.println("Server started on localhost:" + port);
-
- server.start();
}
}
diff --git a/tools/ahat/src/heapdump/HprofFormatException.java b/tools/ahat/src/heapdump/HprofFormatException.java
index 55e8958..0e128cd 100644
--- a/tools/ahat/src/heapdump/HprofFormatException.java
+++ b/tools/ahat/src/heapdump/HprofFormatException.java
@@ -20,4 +20,8 @@
public HprofFormatException(String msg) {
super(msg);
}
+
+ public HprofFormatException(String msg, Exception cause) {
+ super(msg, cause);
+ }
}
diff --git a/tools/ahat/src/heapdump/Parser.java b/tools/ahat/src/heapdump/Parser.java
index 3d5f95f..756b7d2 100644
--- a/tools/ahat/src/heapdump/Parser.java
+++ b/tools/ahat/src/heapdump/Parser.java
@@ -19,6 +19,7 @@
import com.android.ahat.proguard.ProguardMap;
import java.io.File;
import java.io.IOException;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.StandardCharsets;
@@ -48,7 +49,11 @@
*/
public static AhatSnapshot parseHeapDump(File hprof, ProguardMap map)
throws IOException, HprofFormatException {
- return parseHeapDump(new HprofBuffer(hprof), map);
+ try {
+ return parseHeapDump(new HprofBuffer(hprof), map);
+ } catch (BufferUnderflowException e) {
+ throw new HprofFormatException("Unexpected end of file", e);
+ }
}
/**
@@ -56,11 +61,15 @@
*/
public static AhatSnapshot parseHeapDump(ByteBuffer hprof, ProguardMap map)
throws IOException, HprofFormatException {
- return parseHeapDump(new HprofBuffer(hprof), map);
+ try {
+ return parseHeapDump(new HprofBuffer(hprof), map);
+ } catch (BufferUnderflowException e) {
+ throw new HprofFormatException("Unexpected end of file", e);
+ }
}
private static AhatSnapshot parseHeapDump(HprofBuffer hprof, ProguardMap map)
- throws IOException, HprofFormatException {
+ throws IOException, HprofFormatException, BufferUnderflowException {
// Read, and mostly ignore, the hprof header info.
{
StringBuilder format = new StringBuilder();
@@ -409,6 +418,12 @@
break;
}
+ case 0x8a: { // ROOT FINALIZING (ANDROID)
+ long objectId = hprof.getId();
+ roots.add(new RootData(objectId, RootType.FINALIZING));
+ break;
+ }
+
case 0x8b: { // ROOT DEBUGGER (ANDROID)
long objectId = hprof.getId();
roots.add(new RootData(objectId, RootType.DEBUGGER));
diff --git a/tools/ahat/src/heapdump/RootType.java b/tools/ahat/src/heapdump/RootType.java
index 7165b83..af552ea 100644
--- a/tools/ahat/src/heapdump/RootType.java
+++ b/tools/ahat/src/heapdump/RootType.java
@@ -29,7 +29,8 @@
DEBUGGER (1 << 9),
VM_INTERNAL (1 << 10),
UNKNOWN (1 << 11),
- JNI_MONITOR (1 << 12);
+ JNI_MONITOR (1 << 12),
+ FINALIZING (1 << 13);
public final int mask;
diff --git a/tools/ahat/test-dump/L.hprof b/tools/ahat/test-dump/L.hprof
index cf82557..1acdf79 100644
--- a/tools/ahat/test-dump/L.hprof
+++ b/tools/ahat/test-dump/L.hprof
Binary files differ
diff --git a/tools/ahat/test-dump/README.txt b/tools/ahat/test-dump/README.txt
index 344271c..e7ea584 100644
--- a/tools/ahat/test-dump/README.txt
+++ b/tools/ahat/test-dump/README.txt
@@ -1,5 +1,7 @@
Main.java - A program used to generate a heap dump used for tests.
-L.hprof - A version of the test dump generated on Android L.
+L.hprof - A version of the test dump generated on Android L,
+ with one of the ROOT_DEBUGGER records manually changed to a
+ ROOT_FINALIZING record.
O.hprof - A version of the test dump generated on Android O.
RI.hprof - A version of the test dump generated on the reference implementation.