JDWP: allocate single step control only when active
Only allocates SingleStepControl when a single step is requested in
a thread.
Bug: 19142632
Change-Id: I830b7eaea86b516d3fac80ce14d41c66b4bd5415
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 1cbaf39..d89ad5e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -365,19 +365,17 @@
}
void SingleStepControl::VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info) {
- if (method != nullptr) {
- callback(reinterpret_cast<mirror::Object**>(&method), arg, root_info);
+ if (method_ != nullptr) {
+ callback(reinterpret_cast<mirror::Object**>(&method_), arg, root_info);
}
}
-bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
- return dex_pcs.find(dex_pc) == dex_pcs.end();
+void SingleStepControl::AddDexPc(uint32_t dex_pc) {
+ dex_pcs_.insert(dex_pc);
}
-void SingleStepControl::Clear() {
- is_active = false;
- method = nullptr;
- dex_pcs.clear();
+bool SingleStepControl::ContainsDexPc(uint32_t dex_pc) const {
+ return dex_pcs_.find(dex_pc) == dex_pcs_.end();
}
static bool IsBreakpoint(const mirror::ArtMethod* m, uint32_t dex_pc)
@@ -2918,24 +2916,23 @@
// If the debugger is single-stepping one of our threads, check to
// see if we're that thread and we've reached a step point.
const SingleStepControl* single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- if (single_step_control->is_active) {
+ if (single_step_control != nullptr) {
CHECK(!m->IsNative());
- if (single_step_control->step_depth == JDWP::SD_INTO) {
+ if (single_step_control->GetStepDepth() == JDWP::SD_INTO) {
// Step into method calls. We break when the line number
// or method pointer changes. If we're in SS_MIN mode, we
// always stop.
- if (single_step_control->method != m) {
+ if (single_step_control->GetMethod() != m) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new method";
- } else if (single_step_control->step_size == JDWP::SS_MIN) {
+ } else if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new instruction";
} else if (single_step_control->ContainsDexPc(dex_pc)) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new line";
}
- } else if (single_step_control->step_depth == JDWP::SD_OVER) {
+ } else if (single_step_control->GetStepDepth() == JDWP::SD_OVER) {
// Step over method calls. We break when the line number is
// different and the frame depth is <= the original frame
// depth. (We can't just compare on the method, because we
@@ -2944,13 +2941,13 @@
int stack_depth = GetStackDepth(thread);
- if (stack_depth < single_step_control->stack_depth) {
+ if (stack_depth < single_step_control->GetStackDepth()) {
// Popped up one or more frames, always trigger.
event_flags |= kSingleStep;
VLOG(jdwp) << "SS method pop";
- } else if (stack_depth == single_step_control->stack_depth) {
+ } else if (stack_depth == single_step_control->GetStackDepth()) {
// Same depth, see if we moved.
- if (single_step_control->step_size == JDWP::SS_MIN) {
+ if (single_step_control->GetStepSize() == JDWP::SS_MIN) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS new instruction";
} else if (single_step_control->ContainsDexPc(dex_pc)) {
@@ -2959,7 +2956,7 @@
}
}
} else {
- CHECK_EQ(single_step_control->step_depth, JDWP::SD_OUT);
+ CHECK_EQ(single_step_control->GetStepDepth(), JDWP::SD_OUT);
// Return from the current method. We break when the frame
// depth pops up.
@@ -2968,7 +2965,7 @@
// function, rather than the end of the returning function.
int stack_depth = GetStackDepth(thread);
- if (stack_depth < single_step_control->stack_depth) {
+ if (stack_depth < single_step_control->GetStackDepth()) {
event_flags |= kSingleStep;
VLOG(jdwp) << "SS method pop";
}
@@ -3446,20 +3443,11 @@
return sts.GetError();
}
- //
- // Work out what Method* we're in, the current line number, and how deep the stack currently
+ // Work out what ArtMethod* we're in, the current line number, and how deep the stack currently
// is for step-out.
- //
-
struct SingleStepStackVisitor : public StackVisitor {
- explicit SingleStepStackVisitor(Thread* thread, SingleStepControl* single_step_control,
- int32_t* line_number)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- : StackVisitor(thread, nullptr), single_step_control_(single_step_control),
- line_number_(line_number) {
- DCHECK_EQ(single_step_control_, thread->GetSingleStepControl());
- single_step_control_->method = nullptr;
- single_step_control_->stack_depth = 0;
+ explicit SingleStepStackVisitor(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+ : StackVisitor(thread, nullptr), stack_depth(0), method(nullptr), line_number(-1) {
}
// TODO: Enable annotalysis. We know lock is held in constructor, but abstraction confuses
@@ -3467,38 +3455,32 @@
bool VisitFrame() NO_THREAD_SAFETY_ANALYSIS {
mirror::ArtMethod* m = GetMethod();
if (!m->IsRuntimeMethod()) {
- ++single_step_control_->stack_depth;
- if (single_step_control_->method == nullptr) {
+ ++stack_depth;
+ if (method == nullptr) {
mirror::DexCache* dex_cache = m->GetDeclaringClass()->GetDexCache();
- single_step_control_->method = m;
- *line_number_ = -1;
+ method = m;
if (dex_cache != nullptr) {
const DexFile& dex_file = *dex_cache->GetDexFile();
- *line_number_ = dex_file.GetLineNumFromPC(m, GetDexPc());
+ line_number = dex_file.GetLineNumFromPC(m, GetDexPc());
}
}
}
return true;
}
- SingleStepControl* const single_step_control_;
- int32_t* const line_number_;
+ int stack_depth;
+ mirror::ArtMethod* method;
+ int32_t line_number;
};
Thread* const thread = sts.GetThread();
- SingleStepControl* const single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- int32_t line_number = -1;
- SingleStepStackVisitor visitor(thread, single_step_control, &line_number);
+ SingleStepStackVisitor visitor(thread);
visitor.WalkStack();
- //
// Find the dex_pc values that correspond to the current line, for line-based single-stepping.
- //
-
struct DebugCallbackContext {
- explicit DebugCallbackContext(SingleStepControl* single_step_control_cb, int32_t line_number_cb,
- const DexFile::CodeItem* code_item)
+ explicit DebugCallbackContext(SingleStepControl* single_step_control_cb,
+ int32_t line_number_cb, const DexFile::CodeItem* code_item)
: single_step_control_(single_step_control_cb), line_number_(line_number_cb),
code_item_(code_item), last_pc_valid(false), last_pc(0) {
}
@@ -3516,7 +3498,7 @@
} else if (context->last_pc_valid) { // and the line number is new
// Add everything from the last entry up until here to the set
for (uint32_t dex_pc = context->last_pc; dex_pc < address; ++dex_pc) {
- context->single_step_control_->dex_pcs.insert(dex_pc);
+ context->single_step_control_->AddDexPc(dex_pc);
}
context->last_pc_valid = false;
}
@@ -3528,7 +3510,7 @@
if (last_pc_valid) {
size_t end = code_item_->insns_size_in_code_units_;
for (uint32_t dex_pc = last_pc; dex_pc < end; ++dex_pc) {
- single_step_control_->dex_pcs.insert(dex_pc);
+ single_step_control_->AddDexPc(dex_pc);
}
}
}
@@ -3539,8 +3521,14 @@
bool last_pc_valid;
uint32_t last_pc;
};
- single_step_control->dex_pcs.clear();
- mirror::ArtMethod* m = single_step_control->method;
+
+ // Allocate single step.
+ SingleStepControl* single_step_control = new SingleStepControl(step_size, step_depth,
+ visitor.stack_depth,
+ visitor.method);
+ CHECK(single_step_control != nullptr) << "Failed to allocate SingleStepControl";
+ mirror::ArtMethod* m = single_step_control->GetMethod();
+ const int32_t line_number = visitor.line_number;
if (!m->IsNative()) {
const DexFile::CodeItem* const code_item = m->GetCodeItem();
DebugCallbackContext context(single_step_control, line_number, code_item);
@@ -3548,23 +3536,18 @@
DebugCallbackContext::Callback, nullptr, &context);
}
- //
- // Everything else...
- //
-
- single_step_control->step_size = step_size;
- single_step_control->step_depth = step_depth;
- single_step_control->is_active = true;
+ // Activate single-step in the thread.
+ thread->ActivateSingleStepControl(single_step_control);
if (VLOG_IS_ON(jdwp)) {
VLOG(jdwp) << "Single-step thread: " << *thread;
- VLOG(jdwp) << "Single-step step size: " << single_step_control->step_size;
- VLOG(jdwp) << "Single-step step depth: " << single_step_control->step_depth;
- VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->method);
+ VLOG(jdwp) << "Single-step step size: " << single_step_control->GetStepSize();
+ VLOG(jdwp) << "Single-step step depth: " << single_step_control->GetStepDepth();
+ VLOG(jdwp) << "Single-step current method: " << PrettyMethod(single_step_control->GetMethod());
VLOG(jdwp) << "Single-step current line: " << line_number;
- VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->stack_depth;
+ VLOG(jdwp) << "Single-step current stack depth: " << single_step_control->GetStackDepth();
VLOG(jdwp) << "Single-step dex_pc values:";
- for (uint32_t dex_pc : single_step_control->dex_pcs) {
+ for (uint32_t dex_pc : single_step_control->GetDexPcs()) {
VLOG(jdwp) << StringPrintf(" %#x", dex_pc);
}
}
@@ -3578,9 +3561,7 @@
JDWP::JdwpError error;
Thread* thread = DecodeThread(soa, thread_id, &error);
if (error == JDWP::ERR_NONE) {
- SingleStepControl* single_step_control = thread->GetSingleStepControl();
- DCHECK(single_step_control != nullptr);
- single_step_control->Clear();
+ thread->DeactivateSingleStepControl();
}
}
diff --git a/runtime/debugger.h b/runtime/debugger.h
index e79e8e4..901d3e7 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -98,39 +98,58 @@
};
// Thread local data-structure that holds fields for controlling single-stepping.
-struct SingleStepControl {
- SingleStepControl()
- : is_active(false), step_size(JDWP::SS_MIN), step_depth(JDWP::SD_INTO),
- method(nullptr), stack_depth(0) {
+class SingleStepControl {
+ public:
+ SingleStepControl(JDWP::JdwpStepSize step_size, JDWP::JdwpStepDepth step_depth,
+ int stack_depth, mirror::ArtMethod* method)
+ : step_size_(step_size), step_depth_(step_depth),
+ stack_depth_(stack_depth), method_(method) {
}
- // Are we single-stepping right now?
- bool is_active;
+ JDWP::JdwpStepSize GetStepSize() const {
+ return step_size_;
+ }
+ JDWP::JdwpStepDepth GetStepDepth() const {
+ return step_depth_;
+ }
+
+ int GetStackDepth() const {
+ return stack_depth_;
+ }
+
+ mirror::ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ const std::set<uint32_t>& GetDexPcs() const {
+ return dex_pcs_;
+ }
+
+ void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ void AddDexPc(uint32_t dex_pc);
+
+ bool ContainsDexPc(uint32_t dex_pc) const;
+
+ private:
// See JdwpStepSize and JdwpStepDepth for details.
- JDWP::JdwpStepSize step_size;
- JDWP::JdwpStepDepth step_depth;
+ const JDWP::JdwpStepSize step_size_;
+ const JDWP::JdwpStepDepth step_depth_;
+
+ // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
+ // single-step depth.
+ const int stack_depth_;
// The location this single-step was initiated from.
// A single-step is initiated in a suspended thread. We save here the current method and the
// set of DEX pcs associated to the source line number where the suspension occurred.
// This is used to support SD_INTO and SD_OVER single-step depths so we detect when a single-step
// causes the execution of an instruction in a different method or at a different line number.
- mirror::ArtMethod* method;
- std::set<uint32_t> dex_pcs;
+ mirror::ArtMethod* method_;
+ std::set<uint32_t> dex_pcs_;
- // The stack depth when this single-step was initiated. This is used to support SD_OVER and SD_OUT
- // single-step depth.
- int stack_depth;
-
- void VisitRoots(RootCallback* callback, void* arg, const RootInfo& root_info)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
- bool ContainsDexPc(uint32_t dex_pc) const;
-
- void Clear();
-
- private:
DISALLOW_COPY_AND_ASSIGN(SingleStepControl);
};
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 17dfd8c..16edab3 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1149,7 +1149,7 @@
wait_mutex_ = new Mutex("a thread wait mutex");
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.debug_invoke_req = new DebugInvokeReq;
- tlsPtr_.single_step_control = new SingleStepControl;
+ tlsPtr_.single_step_control = nullptr;
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
tlsPtr_.name = new std::string(kThreadNameDuringStartup);
tlsPtr_.nested_signal_state = static_cast<jmp_buf*>(malloc(sizeof(jmp_buf)));
@@ -1302,7 +1302,9 @@
}
delete tlsPtr_.debug_invoke_req;
- delete tlsPtr_.single_step_control;
+ if (tlsPtr_.single_step_control != nullptr) {
+ delete tlsPtr_.single_step_control;
+ }
delete tlsPtr_.instrumentation_stack;
delete tlsPtr_.name;
delete tlsPtr_.stack_trace_sample;
@@ -2420,5 +2422,19 @@
return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0;
}
+void Thread::ActivateSingleStepControl(SingleStepControl* ssc) {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetSingleStepControl() == nullptr) << "Single step already active in thread " << *this;
+ CHECK(ssc != nullptr);
+ tlsPtr_.single_step_control = ssc;
+}
+
+void Thread::DeactivateSingleStepControl() {
+ CHECK(Dbg::IsDebuggerActive());
+ CHECK(GetSingleStepControl() != nullptr) << "Single step not active in thread " << *this;
+ SingleStepControl* ssc = GetSingleStepControl();
+ tlsPtr_.single_step_control = nullptr;
+ delete ssc;
+}
} // namespace art
diff --git a/runtime/thread.h b/runtime/thread.h
index 7db9ba5..c27569e 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -75,7 +75,7 @@
class Runtime;
class ScopedObjectAccessAlreadyRunnable;
class ShadowFrame;
-struct SingleStepControl;
+class SingleStepControl;
class Thread;
class ThreadList;
@@ -709,6 +709,15 @@
return tlsPtr_.single_step_control;
}
+ // Activates single step control for debugging. The thread takes the
+ // ownership of the given SingleStepControl*. It is deleted by a call
+ // to DeactivateSingleStepControl or upon thread destruction.
+ void ActivateSingleStepControl(SingleStepControl* ssc);
+
+ // Deactivates single step control for debugging.
+ void DeactivateSingleStepControl();
+
+
// Returns the fake exception used to activate deoptimization.
static mirror::Throwable* GetDeoptimizationException() {
return reinterpret_cast<mirror::Throwable*>(-1);