summaryrefslogtreecommitdiffstats
path: root/runtime/instrumentation.cc
diff options
context:
space:
mode:
authorSebastien Hertz <shertz@google.com>2014-06-11 19:45:05 +0200
committerSebastien Hertz <shertz@google.com>2014-07-10 16:14:16 +0200
commit320deb2ebe5cce96ca2779875c82853182326685 (patch)
tree53d3e1eae997e09d38e864cf55c6bd4e030a5094 /runtime/instrumentation.cc
parent5fa647d5f663033e4ed3d398aece1f8211d7f460 (diff)
downloadart-320deb2ebe5cce96ca2779875c82853182326685.tar.gz
art-320deb2ebe5cce96ca2779875c82853182326685.tar.bz2
art-320deb2ebe5cce96ca2779875c82853182326685.zip
Fix missing single-step event
During debugging, we used to suspend too lately after a step. It occurred when we were stepping out of an interpreted method into a compiled "caller" method. The issue is we did not deoptimize when returning from the interpreted method but only did it when returning from the compiled method. Therefore we were not executing the rest of the compiled method's code with interpreter which prevents from debugging it. This CL fixes this issue by using instrumentation entry/exit stubs when calling interpreted method from compiled code. Therefore, we execute instrumentation exit stub when returning from interpreter and are able to deoptimize from this point. This allows to execute compiled method's code with interpreter and to debug it. We now also prevent from reporting method entry/exit twice while instrumenting interpreted methods. We report method entry/exit events only from interpreter. Bug: 14422182 Bug: 11705760 Change-Id: Ia1175d36202239273083c4e9733c7e9290244090
Diffstat (limited to 'runtime/instrumentation.cc')
-rw-r--r--runtime/instrumentation.cc80
1 files changed, 53 insertions, 27 deletions
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index f459b590eb..0e05b62dde 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -146,16 +146,13 @@ void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) {
// class, all its static methods code will be set to the instrumentation entry point.
// For more details, see ClassLinker::FixupStaticTrampolines.
if (is_class_initialized || !method->IsStatic() || method->IsConstructor()) {
- // Do not overwrite interpreter to prevent from posting method entry/exit events twice.
- new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
- new_quick_code = class_linker->GetQuickOatCodeFor(method);
- DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
- if (entry_exit_stubs_installed_ && new_quick_code != GetQuickToInterpreterBridge()) {
- // TODO: portable to quick bridge. Bug: 8196384. We cannot enable the check below as long
- // as GetPortableToQuickBridge() == GetPortableToInterpreterBridge().
- // DCHECK(new_portable_code != GetPortableToInterpreterBridge());
+ if (entry_exit_stubs_installed_) {
new_portable_code = GetPortableToInterpreterBridge();
new_quick_code = GetQuickInstrumentationEntryPoint();
+ } else {
+ new_portable_code = class_linker->GetPortableOatCodeFor(method, &have_portable_code);
+ new_quick_code = class_linker->GetQuickOatCodeFor(method);
+ DCHECK(new_quick_code != GetQuickToInterpreterBridgeTrampoline(class_linker));
}
} else {
new_portable_code = GetPortableResolutionTrampoline(class_linker);
@@ -175,7 +172,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
struct InstallStackVisitor : public StackVisitor {
InstallStackVisitor(Thread* thread, Context* context, uintptr_t instrumentation_exit_pc)
: StackVisitor(thread, context), instrumentation_stack_(thread->GetInstrumentationStack()),
- existing_instrumentation_frames_count_(instrumentation_stack_->size()),
instrumentation_exit_pc_(instrumentation_exit_pc),
reached_existing_instrumentation_frames_(false), instrumentation_stack_depth_(0),
last_return_pc_(0) {
@@ -190,18 +186,10 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
last_return_pc_ = 0;
return true; // Ignore upcalls.
}
- if (m->IsRuntimeMethod()) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId();
- }
- last_return_pc_ = GetReturnPc();
- return true; // Ignore unresolved methods since they will be instrumented after resolution.
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Installing exit stub in " << DescribeLocation();
- }
if (GetCurrentQuickFrame() == NULL) {
- InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(), false);
+ bool interpreter_frame = !m->IsPortableCompiled();
+ InstrumentationStackFrame instrumentation_frame(GetThisObject(), m, 0, GetFrameId(),
+ interpreter_frame);
if (kVerboseInstrumentation) {
LOG(INFO) << "Pushing shadow frame " << instrumentation_frame.Dump();
}
@@ -209,6 +197,32 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
return true; // Continue.
}
uintptr_t return_pc = GetReturnPc();
+ if (m->IsRuntimeMethod()) {
+ if (return_pc == instrumentation_exit_pc_) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Handling quick to interpreter transition. Frame " << GetFrameId();
+ }
+ CHECK_LT(instrumentation_stack_depth_, instrumentation_stack_->size());
+ const InstrumentationStackFrame& frame = instrumentation_stack_->at(instrumentation_stack_depth_);
+ CHECK(frame.interpreter_entry_);
+ // This is an interpreter frame so method enter event must have been reported. However we
+ // need to push a DEX pc into the dex_pcs_ list to match size of instrumentation stack.
+ // Since we won't report method entry here, we can safely push any DEX pc.
+ dex_pcs_.push_back(0);
+ last_return_pc_ = frame.return_pc_;
+ ++instrumentation_stack_depth_;
+ return true;
+ } else {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Skipping runtime method. Frame " << GetFrameId();
+ }
+ last_return_pc_ = GetReturnPc();
+ return true; // Ignore unresolved methods since they will be instrumented after resolution.
+ }
+ }
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << " Installing exit stub in " << DescribeLocation();
+ }
if (return_pc == instrumentation_exit_pc_) {
// We've reached a frame which has already been installed with instrumentation exit stub.
// We should have already installed instrumentation on previous frames.
@@ -231,8 +245,15 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
}
- // Insert frame before old ones so we do not corrupt the instrumentation stack.
- auto it = instrumentation_stack_->end() - existing_instrumentation_frames_count_;
+ // Insert frame at the right position so we do not corrupt the instrumentation stack.
+ // Instrumentation stack frames are in descending frame id order.
+ auto it = instrumentation_stack_->begin();
+ for (auto end = instrumentation_stack_->end(); it != end; ++it) {
+ const InstrumentationStackFrame& current = *it;
+ if (instrumentation_frame.frame_id_ >= current.frame_id_) {
+ break;
+ }
+ }
instrumentation_stack_->insert(it, instrumentation_frame);
SetReturnPc(instrumentation_exit_pc_);
}
@@ -243,7 +264,6 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
}
std::deque<InstrumentationStackFrame>* const instrumentation_stack_;
std::vector<InstrumentationStackFrame> shadow_stack_;
- const size_t existing_instrumentation_frames_count_;
std::vector<uint32_t> dex_pcs_;
const uintptr_t instrumentation_exit_pc_;
bool reached_existing_instrumentation_frames_;
@@ -275,7 +295,9 @@ static void InstrumentationInstallStack(Thread* thread, void* arg)
}
uint32_t dex_pc = visitor.dex_pcs_.back();
visitor.dex_pcs_.pop_back();
- instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc);
+ if (!isi->interpreter_entry_) {
+ instrumentation->MethodEnterEvent(thread, (*isi).this_object_, (*isi).method_, dex_pc);
+ }
}
}
thread->VerifyStack();
@@ -606,7 +628,7 @@ void Instrumentation::Deoptimize(mirror::ArtMethod* method) {
CHECK(!already_deoptimized) << "Method " << PrettyMethod(method) << " is already deoptimized";
if (!interpreter_stubs_installed_) {
- UpdateEntrypoints(method, GetQuickToInterpreterBridge(), GetPortableToInterpreterBridge(),
+ UpdateEntrypoints(method, GetQuickInstrumentationEntryPoint(), GetPortableToInterpreterBridge(),
false);
// Install instrumentation exit stub and instrumentation frames. We may already have installed
@@ -844,7 +866,9 @@ void Instrumentation::PushInstrumentationStackFrame(Thread* self, mirror::Object
frame_id, interpreter_entry);
stack->push_front(instrumentation_frame);
- MethodEnterEvent(self, this_object, method, 0);
+ if (!interpreter_entry) {
+ MethodEnterEvent(self, this_object, method, 0);
+ }
}
TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc,
@@ -875,7 +899,9 @@ TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self, uintpt
// return_pc.
uint32_t dex_pc = DexFile::kDexNoIndex;
mirror::Object* this_object = instrumentation_frame.this_object_;
- MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
+ if (!instrumentation_frame.interpreter_entry_) {
+ MethodExitEvent(self, this_object, instrumentation_frame.method_, dex_pc, return_value);
+ }
// Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get
// back to an upcall.