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);