Merge "ART: Generalize "x >> (s & m)" simplification."
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/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 7ddf1c1..1f644c1 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -311,7 +311,11 @@
{
ProfileCompilationInfo profile;
VisitLibcoreDexes([&profile](MethodReference ref) {
- EXPECT_TRUE(profile.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot, ref));
+ uint32_t flags = ProfileCompilationInfo::MethodHotness::kFlagHot |
+ ProfileCompilationInfo::MethodHotness::kFlagStartup;
+ EXPECT_TRUE(profile.AddMethodIndex(
+ static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
+ ref));
}, [&profile](TypeReference ref) {
EXPECT_TRUE(profile.AddClassForDex(ref));
}, kMethodFrequency, kTypeFrequency);
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index a80dbf6..dfbe31a 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -961,8 +961,8 @@
if (method_hotness.IsHot() &&
!method_hotness.IsStartup() && !method_hotness.IsPostStartup()) {
std::string name = method_reference.PrettyMethod();
- LOG(WARNING) << "Method " << name << " had a Hot method that wasn't marked "
- << "either start-up or post-startup. Possible corrupted profile?";
+ LOG(FATAL) << "Method " << name << " had a Hot method that wasn't marked "
+ << "either start-up or post-startup. Possible corrupted profile?";
// This is not fatal, so only warn.
}
}
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/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 6903af2..80fdadb 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -188,9 +188,7 @@
const uint32_t* vreg_ptr = &vregs_[i];
ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
}
- if (kUseReadBarrier) {
- ReadBarrier::AssertToSpaceInvariant(ref);
- }
+ ReadBarrier::MaybeAssertToSpaceInvariant(ref);
if (kVerifyFlags & kVerifyReads) {
VerifyObject(ref);
}
@@ -256,9 +254,7 @@
if (kVerifyFlags & kVerifyWrites) {
VerifyObject(val);
}
- if (kUseReadBarrier) {
- ReadBarrier::AssertToSpaceInvariant(val);
- }
+ ReadBarrier::MaybeAssertToSpaceInvariant(val);
uint32_t* vreg = &vregs_[i];
reinterpret_cast<StackReference<mirror::Object>*>(vreg)->Assign(val);
if (HasReferenceArray()) {
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/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc
index 1ed7889..5601317 100644
--- a/runtime/jit/profile_compilation_info.cc
+++ b/runtime/jit/profile_compilation_info.cc
@@ -1704,7 +1704,10 @@
if (m < (number_of_methods / kFavorSplit)) {
method_idx %= kFavorFirstN;
}
- info.AddMethodIndex(MethodHotness::kFlagHot,
+ // Alternate between startup and post startup.
+ uint32_t flags = MethodHotness::kFlagHot;
+ flags |= ((m & 1) != 0) ? MethodHotness::kFlagPostStartup : MethodHotness::kFlagStartup;
+ info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
profile_key,
/*method_idx*/ 0,
method_idx,
@@ -1761,8 +1764,13 @@
if (number_of_methods - i == methods_required_in_profile ||
std::rand() % (number_of_methods - i - methods_required_in_profile) == 0) {
uint32_t method_index = (method_start_index + i) % number_of_methods;
- info.AddMethodIndex(MethodHotness::kFlagHot, MethodReference(dex_file.get(),
- method_index));
+ // Alternate between startup and post startup.
+ uint32_t flags = MethodHotness::kFlagHot;
+ flags |= ((method_index & 1) != 0)
+ ? MethodHotness::kFlagPostStartup
+ : MethodHotness::kFlagStartup;
+ info.AddMethodIndex(static_cast<MethodHotness::Flag>(flags),
+ MethodReference(dex_file.get(), method_index));
methods_required_in_profile--;
}
}
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b2f6c03..9a42c29 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -635,29 +635,35 @@
}
if (boot_image_tables != nullptr) {
- // Map boot image tables into the .bss. The reserved size must match size of the tables.
- size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
- size_t tables_size = 0u;
- for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
- tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
- DCHECK_ALIGNED(tables_size, kPageSize);
- }
- if (tables_size != reserved_size) {
- *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
- " %zu bytes, should be %zu.",
- GetLocation().c_str(),
- reserved_size,
- tables_size);
- return false;
- }
- for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
- uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
- if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(runtime == nullptr)) {
+ // This must be oatdump without boot image. Make sure the .bss is inaccessible.
+ mprotect(const_cast<uint8_t*>(BssBegin()), BssSize(), PROT_NONE);
+ } else {
+ // Map boot image tables into the .bss. The reserved size must match size of the tables.
+ size_t reserved_size = static_cast<size_t>(boot_image_tables_end - boot_image_tables);
+ size_t tables_size = 0u;
+ for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
+ tables_size += space->GetImageHeader().GetBootImageConstantTablesSize();
+ DCHECK_ALIGNED(tables_size, kPageSize);
+ }
+ if (tables_size != reserved_size) {
+ *error_msg = StringPrintf("In oat file '%s' found unexpected boot image table sizes, "
+ " %zu bytes, should be %zu.",
+ GetLocation().c_str(),
+ reserved_size,
+ tables_size);
return false;
}
- boot_image_tables += current_tables_size;
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ uint32_t current_tables_size = space->GetImageHeader().GetBootImageConstantTablesSize();
+ if (current_tables_size != 0u && !MapConstantTables(space, boot_image_tables)) {
+ return false;
+ }
+ boot_image_tables += current_tables_size;
+ }
+ DCHECK(boot_image_tables == boot_image_tables_end);
}
- DCHECK(boot_image_tables == boot_image_tables_end);
}
return true;
}
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/read_barrier.h b/runtime/read_barrier.h
index 45e78bc..00674b2 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -89,6 +89,14 @@
static void AssertToSpaceInvariant(GcRootSource* gc_root_source, mirror::Object* ref)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Without the holder object, and only with the read barrier configuration (no-op otherwise).
+ static void MaybeAssertToSpaceInvariant(mirror::Object* ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kUseReadBarrier) {
+ AssertToSpaceInvariant(ref);
+ }
+ }
+
// ALWAYS_INLINE on this caused a performance regression b/26744236.
static mirror::Object* Mark(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
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..524e73d 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2159,11 +2159,11 @@
ScopedObjectAccess soa(self);
// We may need to call user-supplied managed code, do this before final clean-up.
HandleUncaughtExceptions(soa);
+ RemoveFromThreadGroup(soa);
Runtime* runtime = Runtime::Current();
if (runtime != nullptr) {
runtime->GetRuntimeCallbacks()->ThreadDeath(self);
}
- RemoveFromThreadGroup(soa);
// this.nativePeer = 0;
if (Runtime::Current()->IsActiveTransaction()) {
@@ -3080,6 +3080,8 @@
UNREACHABLE();
}
+ ReadBarrier::MaybeAssertToSpaceInvariant(exception.Ptr());
+
// This is a real exception: let the instrumentation know about it.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
if (instrumentation->HasExceptionThrownListeners() &&
@@ -3121,6 +3123,15 @@
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());
+ // Check the to-space invariant on the re-installed exception (if applicable).
+ ReadBarrier::MaybeAssertToSpaceInvariant(GetException());
+ }
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/1923-frame-pop/src/art/Trace.java b/test/1923-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1923-frame-pop/src/art/Trace.java
+++ b/test/1923-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/1924-frame-pop-toggle/src/art/Trace.java b/test/1924-frame-pop-toggle/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1924-frame-pop-toggle/src/art/Trace.java
+++ b/test/1924-frame-pop-toggle/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/1925-self-frame-pop/src/art/Trace.java b/test/1925-self-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1925-self-frame-pop/src/art/Trace.java
+++ b/test/1925-self-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/1926-missed-frame-pop/src/art/Trace.java b/test/1926-missed-frame-pop/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/1926-missed-frame-pop/src/art/Trace.java
+++ b/test/1926-missed-frame-pop/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
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/1936-thread-end-events/check b/test/1936-thread-end-events/check
new file mode 100644
index 0000000..8a84388
--- /dev/null
+++ b/test/1936-thread-end-events/check
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+# The RI sends an extra event that art doesn't. Add it to the expected output.
+if [[ "$TEST_RUNTIME" == "jvm" ]]; then
+ patch -p0 expected.txt < jvm-expected.patch >/dev/null
+fi
+
+./default-check "$@"
diff --git a/test/1936-thread-end-events/expected.txt b/test/1936-thread-end-events/expected.txt
new file mode 100644
index 0000000..6b71c5e
--- /dev/null
+++ b/test/1936-thread-end-events/expected.txt
@@ -0,0 +1,42 @@
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered void java.lang.ThreadGroup.threadTerminated(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.ThreadGroup.remove(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static native void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.NotifyThreadEnd(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
diff --git a/test/1936-thread-end-events/info.txt b/test/1936-thread-end-events/info.txt
new file mode 100644
index 0000000..51986c4
--- /dev/null
+++ b/test/1936-thread-end-events/info.txt
@@ -0,0 +1,6 @@
+Tests JVMTI ThreadEnd bug
+
+We had a bug where we were still sending events after JVMTI_EVENT_THREAD_END due
+to where we sent the event. This test ensures that the placement of the
+THREAD_END event is correct.
+
diff --git a/test/1936-thread-end-events/jvm-expected.patch b/test/1936-thread-end-events/jvm-expected.patch
new file mode 100644
index 0000000..ddb30a3
--- /dev/null
+++ b/test/1936-thread-end-events/jvm-expected.patch
@@ -0,0 +1,16 @@
+7a8,14
+> Entered private void java.lang.Thread.exit()
+> Thread: test-thread
+> | alive: true
+> | interrupted: false
+> | daemon: false
+> | group: java.lang.ThreadGroup[name=main,maxpri=10]
+>
+34c41
+< | group: java.lang.ThreadGroup[name=main,maxpri=10]
+---
+> | group: null
+41c48
+< | group: java.lang.ThreadGroup[name=main,maxpri=10]
+---
+> | group: null
diff --git a/test/1936-thread-end-events/method_trace.cc b/test/1936-thread-end-events/method_trace.cc
new file mode 100644
index 0000000..019b6a9
--- /dev/null
+++ b/test/1936-thread-end-events/method_trace.cc
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 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 <inttypes.h>
+
+#include <cstdio>
+#include <memory>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test989StackTraceThrow {
+
+extern "C" JNIEXPORT
+jfloat JNICALL Java_art_Test989_returnFloatNative(JNIEnv* env, jclass klass) {
+ jmethodID targetMethod = env->GetStaticMethodID(klass, "doGetFloat", "()F");
+ return env->CallStaticFloatMethod(klass, targetMethod);
+}
+extern "C" JNIEXPORT
+jdouble JNICALL Java_art_Test989_returnDoubleNative(JNIEnv* env, jclass klass) {
+ jmethodID targetMethod = env->GetStaticMethodID(klass, "doGetDouble", "()D");
+ return env->CallStaticDoubleMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT jobject JNICALL Java_art_Test989_returnValueNative(JNIEnv* env, jclass klass) {
+ jmethodID targetMethod = env->GetStaticMethodID(klass, "mkTestObject", "()Ljava/lang/Object;");
+ return env->CallStaticObjectMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_doNothingNative(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ return;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_throwANative(JNIEnv* env,
+ jclass klass) {
+ jmethodID targetMethod = env->GetStaticMethodID(klass, "doThrowA", "()V");
+ env->CallStaticVoidMethod(klass, targetMethod);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test989_acceptValueNative(JNIEnv* env,
+ jclass klass,
+ jobject arg) {
+ jmethodID targetMethod = env->GetStaticMethodID(klass, "printObject", "(Ljava/lang/Object;)V");
+ env->CallStaticVoidMethod(klass, targetMethod, arg);
+}
+
+} // namespace Test989StackTraceThrow
+} // namespace art
+
diff --git a/test/1936-thread-end-events/run b/test/1936-thread-end-events/run
new file mode 100755
index 0000000..51875a7
--- /dev/null
+++ b/test/1936-thread-end-events/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/1936-thread-end-events/src/Main.java b/test/1936-thread-end-events/src/Main.java
new file mode 100644
index 0000000..da66fc3
--- /dev/null
+++ b/test/1936-thread-end-events/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1936.run();
+ }
+}
diff --git a/test/1936-thread-end-events/src/art/Test1936.java b/test/1936-thread-end-events/src/art/Test1936.java
new file mode 100644
index 0000000..868deca
--- /dev/null
+++ b/test/1936-thread-end-events/src/art/Test1936.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Test1936 {
+ public static void foo() {}
+
+ public static void NotifyThreadEnd(Thread me) {
+ // Don't actually do anything.
+ foo();
+ }
+
+ public static void NotifyMethodEntry(Object o) {
+ System.out.println("Entered " + o.toString());
+ Thread me = Thread.currentThread();
+ System.out.println(String.format(
+ "Thread: %s\n" +
+ " | alive: %b\n" +
+ " | interrupted: %b\n" +
+ " | daemon: %b\n" +
+ " | group: %s\n",
+ me.getName(), me.isAlive(), me.isInterrupted(), me.isDaemon(), me.getThreadGroup()));
+ }
+
+ public static native void waitForever();
+ private static void setupTracing(Thread target) throws Exception {
+ Trace.disableTracing(target);
+ Trace.enableTracing2(
+ Test1936.class,
+ Test1936.class.getDeclaredMethod("NotifyMethodEntry", Object.class),
+ /*exit*/null,
+ /*field_access*/null,
+ /*field_modify*/null,
+ /*single_step*/null,
+ /*thread_start*/null,
+ Test1936.class.getDeclaredMethod("NotifyThreadEnd", Thread.class),
+ target);
+ }
+
+
+ public static void run() throws Exception {
+ Thread t = new Thread(() -> {
+ try {
+ setupTracing(Thread.currentThread());
+ foo();
+ } catch (Exception e) {
+ System.out.println("Caught exception " + e + "!");
+ e.printStackTrace();
+ }
+ }, "test-thread");
+ t.start();
+ t.join();
+ }
+}
diff --git a/test/1936-thread-end-events/src/art/Trace.java b/test/1936-thread-end-events/src/art/Trace.java
new file mode 100644
index 0000000..8999bb1
--- /dev/null
+++ b/test/1936-thread-end-events/src/art/Trace.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.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
+}
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
index 1ca6d7b..7756a16 100644
--- a/test/638-checker-inline-caches/profile
+++ b/test/638-checker-inline-caches/profile
@@ -1,6 +1,6 @@
-LMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
-LMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
-LMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
-LMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
-LMain;->inlineMissingTypes(LSuper;)I+missing_types
-LMain;->noInlineCache(LSuper;)I
+HSLMain;->inlineMonomorphicSubA(LSuper;)I+LSubA;
+HSLMain;->inlinePolymophicSubASubB(LSuper;)I+LSubA;,LSubB;
+HSLMain;->inlinePolymophicCrossDexSubASubC(LSuper;)I+LSubA;,LSubC;
+HSLMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
+HSLMain;->inlineMissingTypes(LSuper;)I+missing_types
+HSLMain;->noInlineCache(LSuper;)I
diff --git a/test/643-checker-bogus-ic/profile b/test/643-checker-bogus-ic/profile
index cbf7796..540a935 100644
--- a/test/643-checker-bogus-ic/profile
+++ b/test/643-checker-bogus-ic/profile
@@ -1,2 +1,2 @@
-LMain;->inlineMonomorphic(LMain;)I+LUnrelated;
-LMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
+SHLMain;->inlineMonomorphic(LMain;)I+LUnrelated;
+SHLMain;->inlinePolymorphic(LMain;)I+LUnrelated;,LMain;
diff --git a/test/648-inline-caches-unresolved/profile b/test/648-inline-caches-unresolved/profile
index 92c0a41..06bc8ad 100644
--- a/test/648-inline-caches-unresolved/profile
+++ b/test/648-inline-caches-unresolved/profile
@@ -1 +1 @@
-LMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
+SHLMain;->inlineMonomorphicUnresolvedSuper(Ljava/lang/Object;)Ljava/lang/String;+LSubclass;
diff --git a/test/661-oat-writer-layout/expected.txt b/test/661-oat-writer-layout/expected.txt
index db28e4f..b7ad70a 100644
--- a/test/661-oat-writer-layout/expected.txt
+++ b/test/661-oat-writer-layout/expected.txt
@@ -8,15 +8,6 @@
C::m_a$$$
C::m_b$$$
C::m_c$$$
-A::m_a$Hot$$
-A::m_b$Hot$$
-A::m_c$Hot$$
-B::m_a$Hot$$
-B::m_b$Hot$$
-B::m_c$Hot$$
-C::m_a$Hot$$
-C::m_b$Hot$$
-C::m_c$Hot$$
A::m_a$$Startup$
A::m_b$$Startup$
A::m_c$$Startup$
diff --git a/test/661-oat-writer-layout/profile b/test/661-oat-writer-layout/profile
index 5406484..cf307c2 100644
--- a/test/661-oat-writer-layout/profile
+++ b/test/661-oat-writer-layout/profile
@@ -1,60 +1,51 @@
-HLA;->m_a$Hot$$()V
SLA;->m_a$$Startup$()V
HSLA;->m_a$Hot$Startup$()V
PLA;->m_a$$$Poststartup()V
HPLA;->m_a$Hot$$Poststartup()V
SPLA;->m_a$$Startup$Poststartup()V
HSPLA;->m_a$Hot$Startup$Poststartup()V
-HLA;->m_b$Hot$$()V
SLA;->m_b$$Startup$()V
HSLA;->m_b$Hot$Startup$()V
PLA;->m_b$$$Poststartup()V
HPLA;->m_b$Hot$$Poststartup()V
SPLA;->m_b$$Startup$Poststartup()V
HSPLA;->m_b$Hot$Startup$Poststartup()V
-HLA;->m_c$Hot$$()V
SLA;->m_c$$Startup$()V
HSLA;->m_c$Hot$Startup$()V
PLA;->m_c$$$Poststartup()V
HPLA;->m_c$Hot$$Poststartup()V
SPLA;->m_c$$Startup$Poststartup()V
HSPLA;->m_c$Hot$Startup$Poststartup()V
-HLB;->m_a$Hot$$()V
SLB;->m_a$$Startup$()V
HSLB;->m_a$Hot$Startup$()V
PLB;->m_a$$$Poststartup()V
HPLB;->m_a$Hot$$Poststartup()V
SPLB;->m_a$$Startup$Poststartup()V
HSPLB;->m_a$Hot$Startup$Poststartup()V
-HLB;->m_b$Hot$$()V
SLB;->m_b$$Startup$()V
HSLB;->m_b$Hot$Startup$()V
PLB;->m_b$$$Poststartup()V
HPLB;->m_b$Hot$$Poststartup()V
SPLB;->m_b$$Startup$Poststartup()V
HSPLB;->m_b$Hot$Startup$Poststartup()V
-HLB;->m_c$Hot$$()V
SLB;->m_c$$Startup$()V
HSLB;->m_c$Hot$Startup$()V
PLB;->m_c$$$Poststartup()V
HPLB;->m_c$Hot$$Poststartup()V
SPLB;->m_c$$Startup$Poststartup()V
HSPLB;->m_c$Hot$Startup$Poststartup()V
-HLC;->m_a$Hot$$()V
SLC;->m_a$$Startup$()V
HSLC;->m_a$Hot$Startup$()V
PLC;->m_a$$$Poststartup()V
HPLC;->m_a$Hot$$Poststartup()V
SPLC;->m_a$$Startup$Poststartup()V
HSPLC;->m_a$Hot$Startup$Poststartup()V
-HLC;->m_b$Hot$$()V
SLC;->m_b$$Startup$()V
HSLC;->m_b$Hot$Startup$()V
PLC;->m_b$$$Poststartup()V
HPLC;->m_b$Hot$$Poststartup()V
SPLC;->m_b$$Startup$Poststartup()V
HSPLC;->m_b$Hot$Startup$Poststartup()V
-HLC;->m_c$Hot$$()V
SLC;->m_c$$Startup$()V
HSLC;->m_c$Hot$Startup$()V
PLC;->m_c$$$Poststartup()V
diff --git a/test/661-oat-writer-layout/src/Test.java b/test/661-oat-writer-layout/src/Test.java
index db67b48..f862e37 100644
--- a/test/661-oat-writer-layout/src/Test.java
+++ b/test/661-oat-writer-layout/src/Test.java
@@ -13,86 +13,78 @@
// limitations under the License.
import java.lang.reflect.Method;
+import java.util.ArrayList;
public class Test {
// Returns list of all methods in Generated.java
// This is to avoid having to introspect classes with extra code
// (for example, we ignore <init> methods).
public static Method[] getTestMethods() throws NoSuchMethodException, SecurityException {
- Method[] all_methods = new Method[72];
- all_methods[0] = A.class.getDeclaredMethod("m_a$$$");
- all_methods[1] = A.class.getDeclaredMethod("m_a$Hot$$");
- all_methods[2] = A.class.getDeclaredMethod("m_a$$Startup$");
- all_methods[3] = A.class.getDeclaredMethod("m_a$Hot$Startup$");
- all_methods[4] = A.class.getDeclaredMethod("m_a$$$Poststartup");
- all_methods[5] = A.class.getDeclaredMethod("m_a$Hot$$Poststartup");
- all_methods[6] = A.class.getDeclaredMethod("m_a$$Startup$Poststartup");
- all_methods[7] = A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
- all_methods[8] = A.class.getDeclaredMethod("m_b$$$");
- all_methods[9] = A.class.getDeclaredMethod("m_b$Hot$$");
- all_methods[10] = A.class.getDeclaredMethod("m_b$$Startup$");
- all_methods[11] = A.class.getDeclaredMethod("m_b$Hot$Startup$");
- all_methods[12] = A.class.getDeclaredMethod("m_b$$$Poststartup");
- all_methods[13] = A.class.getDeclaredMethod("m_b$Hot$$Poststartup");
- all_methods[14] = A.class.getDeclaredMethod("m_b$$Startup$Poststartup");
- all_methods[15] = A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
- all_methods[16] = A.class.getDeclaredMethod("m_c$$$");
- all_methods[17] = A.class.getDeclaredMethod("m_c$Hot$$");
- all_methods[18] = A.class.getDeclaredMethod("m_c$$Startup$");
- all_methods[19] = A.class.getDeclaredMethod("m_c$Hot$Startup$");
- all_methods[20] = A.class.getDeclaredMethod("m_c$$$Poststartup");
- all_methods[21] = A.class.getDeclaredMethod("m_c$Hot$$Poststartup");
- all_methods[22] = A.class.getDeclaredMethod("m_c$$Startup$Poststartup");
- all_methods[23] = A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
- all_methods[24] = B.class.getDeclaredMethod("m_a$$$");
- all_methods[25] = B.class.getDeclaredMethod("m_a$Hot$$");
- all_methods[26] = B.class.getDeclaredMethod("m_a$$Startup$");
- all_methods[27] = B.class.getDeclaredMethod("m_a$Hot$Startup$");
- all_methods[28] = B.class.getDeclaredMethod("m_a$$$Poststartup");
- all_methods[29] = B.class.getDeclaredMethod("m_a$Hot$$Poststartup");
- all_methods[30] = B.class.getDeclaredMethod("m_a$$Startup$Poststartup");
- all_methods[31] = B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
- all_methods[32] = B.class.getDeclaredMethod("m_b$$$");
- all_methods[33] = B.class.getDeclaredMethod("m_b$Hot$$");
- all_methods[34] = B.class.getDeclaredMethod("m_b$$Startup$");
- all_methods[35] = B.class.getDeclaredMethod("m_b$Hot$Startup$");
- all_methods[36] = B.class.getDeclaredMethod("m_b$$$Poststartup");
- all_methods[37] = B.class.getDeclaredMethod("m_b$Hot$$Poststartup");
- all_methods[38] = B.class.getDeclaredMethod("m_b$$Startup$Poststartup");
- all_methods[39] = B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
- all_methods[40] = B.class.getDeclaredMethod("m_c$$$");
- all_methods[41] = B.class.getDeclaredMethod("m_c$Hot$$");
- all_methods[42] = B.class.getDeclaredMethod("m_c$$Startup$");
- all_methods[43] = B.class.getDeclaredMethod("m_c$Hot$Startup$");
- all_methods[44] = B.class.getDeclaredMethod("m_c$$$Poststartup");
- all_methods[45] = B.class.getDeclaredMethod("m_c$Hot$$Poststartup");
- all_methods[46] = B.class.getDeclaredMethod("m_c$$Startup$Poststartup");
- all_methods[47] = B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
- all_methods[48] = C.class.getDeclaredMethod("m_a$$$");
- all_methods[49] = C.class.getDeclaredMethod("m_a$Hot$$");
- all_methods[50] = C.class.getDeclaredMethod("m_a$$Startup$");
- all_methods[51] = C.class.getDeclaredMethod("m_a$Hot$Startup$");
- all_methods[52] = C.class.getDeclaredMethod("m_a$$$Poststartup");
- all_methods[53] = C.class.getDeclaredMethod("m_a$Hot$$Poststartup");
- all_methods[54] = C.class.getDeclaredMethod("m_a$$Startup$Poststartup");
- all_methods[55] = C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup");
- all_methods[56] = C.class.getDeclaredMethod("m_b$$$");
- all_methods[57] = C.class.getDeclaredMethod("m_b$Hot$$");
- all_methods[58] = C.class.getDeclaredMethod("m_b$$Startup$");
- all_methods[59] = C.class.getDeclaredMethod("m_b$Hot$Startup$");
- all_methods[60] = C.class.getDeclaredMethod("m_b$$$Poststartup");
- all_methods[61] = C.class.getDeclaredMethod("m_b$Hot$$Poststartup");
- all_methods[62] = C.class.getDeclaredMethod("m_b$$Startup$Poststartup");
- all_methods[63] = C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup");
- all_methods[64] = C.class.getDeclaredMethod("m_c$$$");
- all_methods[65] = C.class.getDeclaredMethod("m_c$Hot$$");
- all_methods[66] = C.class.getDeclaredMethod("m_c$$Startup$");
- all_methods[67] = C.class.getDeclaredMethod("m_c$Hot$Startup$");
- all_methods[68] = C.class.getDeclaredMethod("m_c$$$Poststartup");
- all_methods[69] = C.class.getDeclaredMethod("m_c$Hot$$Poststartup");
- all_methods[70] = C.class.getDeclaredMethod("m_c$$Startup$Poststartup");
- all_methods[71] = C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup");
- return all_methods;
+ ArrayList<Method> all_methods = new ArrayList<Method>();
+ all_methods.add(A.class.getDeclaredMethod("m_a$$$"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$$$"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$$$"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+ all_methods.add(A.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$$$"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$$$"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$$$"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+ all_methods.add(B.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$$$"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$Hot$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_a$Hot$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$$$"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$Hot$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_b$Hot$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$$$"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$Hot$$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$$Startup$Poststartup"));
+ all_methods.add(C.class.getDeclaredMethod("m_c$Hot$Startup$Poststartup"));
+ return all_methods.toArray(new Method[all_methods.size()]);
}
}
diff --git a/test/707-checker-invalid-profile/profile b/test/707-checker-invalid-profile/profile
index 5979dd2..f142c40 100644
--- a/test/707-checker-invalid-profile/profile
+++ b/test/707-checker-invalid-profile/profile
@@ -1,4 +1,4 @@
-LMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
-LMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
-LMain;->invalid_method
+SHLMain;->attemptInlineMonomorphic(LMain;)I+invalid_class
+SHLMain;->attemptInlinePolymorphic(LMain;)I+LMain;,invalid_class
+SHLMain;->invalid_method
invalid_class
\ No newline at end of file
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/990-field-trace/src/art/Trace.java
+++ b/test/990-field-trace/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/991-field-trace-2/src/art/Trace.java
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
diff --git a/test/997-single-step/src/art/Trace.java b/test/997-single-step/src/art/Trace.java
index ba3d397..8999bb1 100644
--- a/test/997-single-step/src/art/Trace.java
+++ b/test/997-single-step/src/art/Trace.java
@@ -53,4 +53,16 @@
public static native void watchFieldModification(Field f);
public static native void watchAllFieldAccesses();
public static native void watchAllFieldModifications();
+
+ // the names, arguments, and even line numbers of these functions are embedded in the tests so we
+ // need to add to the bottom and not modify old ones to maintain compat.
+ public static native void enableTracing2(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Method ThreadStart,
+ Method ThreadEnd,
+ Thread thr);
}
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/test/ti-agent/trace_helper.cc b/test/ti-agent/trace_helper.cc
index 1f8ceff..8b74c7c 100644
--- a/test/ti-agent/trace_helper.cc
+++ b/test/ti-agent/trace_helper.cc
@@ -34,11 +34,36 @@
jmethodID field_access;
jmethodID field_modify;
jmethodID single_step;
+ jmethodID thread_start;
+ jmethodID thread_end;
bool in_callback;
bool access_watch_on_load;
bool modify_watch_on_load;
};
+static void threadStartCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data->thread_start != nullptr);
+ jnienv->CallStaticVoidMethod(data->test_klass, data->thread_start, thread);
+}
+static void threadEndCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data->thread_end != nullptr);
+ jnienv->CallStaticVoidMethod(data->test_klass, data->thread_end, thread);
+}
+
static void singleStepCB(jvmtiEnv* jvmti,
JNIEnv* jnienv,
jthread thread,
@@ -362,7 +387,7 @@
env->DeleteLocalRef(klass);
}
-extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing2(
JNIEnv* env,
jclass trace ATTRIBUTE_UNUSED,
jclass klass,
@@ -371,6 +396,8 @@
jobject field_access,
jobject field_modify,
jobject single_step,
+ jobject thread_start,
+ jobject thread_end,
jthread thr) {
TraceData* data = nullptr;
if (JvmtiErrorToException(env,
@@ -386,6 +413,8 @@
data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
+ data->thread_start = thread_start != nullptr ? env->FromReflectedMethod(thread_start) : nullptr;
+ data->thread_end = thread_end != nullptr ? env->FromReflectedMethod(thread_end) : nullptr;
data->in_callback = false;
TraceData* old_data = nullptr;
@@ -410,6 +439,8 @@
cb.FieldModification = fieldModificationCB;
cb.ClassPrepare = classPrepareCB;
cb.SingleStep = singleStepCB;
+ cb.ThreadStart = threadStartCB;
+ cb.ThreadEnd = threadEndCB;
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
return;
}
@@ -453,6 +484,46 @@
thr))) {
return;
}
+ if (thread_start != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_THREAD_START,
+ thr))) {
+ return;
+ }
+ if (thread_end != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_THREAD_END,
+ thr))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
+ JNIEnv* env,
+ jclass trace,
+ jclass klass,
+ jobject enter,
+ jobject exit,
+ jobject field_access,
+ jobject field_modify,
+ jobject single_step,
+ jthread thr) {
+ Java_art_Trace_enableTracing2(env,
+ trace,
+ klass,
+ enter,
+ exit,
+ field_access,
+ field_modify,
+ single_step,
+ /* thread_start */ nullptr,
+ /* thread_end */ nullptr,
+ thr);
+ return;
}
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
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.