OmarEmaraDev created this revision.
OmarEmaraDev added a reviewer: clayborg.
Herald added a reviewer: teemperor.
OmarEmaraDev requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This patch adds a form window to attach a process, either by PID or by
name. This patch also adds support for dynamic field visibility such
that the form delegate can hide or show certain fields based on some
conditions.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D105655

Files:
  lldb/source/Core/IOHandlerCursesGUI.cpp

Index: lldb/source/Core/IOHandlerCursesGUI.cpp
===================================================================
--- lldb/source/Core/IOHandlerCursesGUI.cpp
+++ lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -608,9 +608,19 @@
       m_delete = del;
     }
   }
-  //
+
   // Get the rectangle in our parent window
   Rect GetBounds() const { return Rect(GetParentOrigin(), GetSize()); }
+
+  Rect GetCenteredRect(int width, int height) {
+    Size size = GetSize();
+    width = std::min(size.width, width);
+    height = std::min(size.height, height);
+    int x = (size.width - width) / 2;
+    int y = (size.height - height) / 2;
+    return Rect(Point(x, y), Size(width, height));
+  }
+
   int GetChar() { return ::wgetch(m_window); }
   Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
   int GetParentX() const { return getparx(m_window); }
@@ -1050,6 +1060,15 @@
 
   // Select the last element in the field if multiple elements exists.
   virtual void FieldDelegateSelectLastElement() { return; }
+
+  bool FieldDelegateIsVisible() { return m_is_visible; }
+
+  void FieldDelegateHide() { m_is_visible = false; }
+
+  void FieldDelegateShow() { m_is_visible = true; }
+
+protected:
+  bool m_is_visible = true;
 };
 
 typedef std::shared_ptr<FieldDelegate> FieldDelegateSP;
@@ -1812,6 +1831,10 @@
 
   virtual ~FormDelegate() = default;
 
+  virtual std::string GetName() = 0;
+
+  virtual void UpdateFieldsVisibility() { return; }
+
   FieldDelegateSP &GetField(int field_index) { return m_fields[field_index]; }
 
   FormAction &GetAction(int action_index) { return m_actions[action_index]; }
@@ -1948,6 +1971,8 @@
     int height = 0;
     height += GetErrorHeight();
     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
+      if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
+        continue;
       height += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
     }
     height += GetActionsHeight();
@@ -1963,6 +1988,8 @@
 
     int offset = GetErrorHeight();
     for (int i = 0; i < m_selection_index; i++) {
+      if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
+        continue;
       offset += m_delegate_sp->GetField(i)->FieldDelegateGetHeight();
     }
     context.Offset(offset);
@@ -2018,6 +2045,8 @@
     int width = surface.GetWidth();
     bool a_field_is_selected = m_selection_type == SelectionType::Field;
     for (int i = 0; i < m_delegate_sp->GetNumberOfFields(); i++) {
+      if (!m_delegate_sp->GetField(i)->FieldDelegateIsVisible())
+        continue;
       bool is_field_selected = a_field_is_selected && m_selection_index == i;
       FieldDelegateSP &field = m_delegate_sp->GetField(i);
       int height = field->FieldDelegateGetHeight();
@@ -2080,10 +2109,12 @@
   }
 
   bool WindowDelegateDraw(Window &window, bool force) override {
+    m_delegate_sp->UpdateFieldsVisibility();
 
     window.Erase();
 
-    window.DrawTitleBox(window.GetName(), "Press Esc to cancel");
+    window.DrawTitleBox(m_delegate_sp->GetName().c_str(),
+                        "Press Esc to cancel");
 
     Rect content_bounds = window.GetFrame();
     content_bounds.Inset(2, 2);
@@ -2093,6 +2124,21 @@
     return true;
   }
 
+  void SkipNextHiddenFields() {
+    while (true) {
+      if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
+        return;
+
+      if (m_selection_index == m_delegate_sp->GetNumberOfFields() - 1) {
+        m_selection_type = SelectionType::Action;
+        m_selection_index = 0;
+        return;
+      }
+
+      m_selection_index++;
+    }
+  }
+
   HandleCharResult SelecteNext(int key) {
     if (m_selection_type == SelectionType::Action) {
       if (m_selection_index < m_delegate_sp->GetNumberOfActions() - 1) {
@@ -2102,8 +2148,12 @@
 
       m_selection_index = 0;
       m_selection_type = SelectionType::Field;
-      FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index);
-      next_field->FieldDelegateSelectFirstElement();
+      SkipNextHiddenFields();
+      if (m_selection_type == SelectionType::Field) {
+        FieldDelegateSP &next_field =
+            m_delegate_sp->GetField(m_selection_index);
+        next_field->FieldDelegateSelectFirstElement();
+      }
       return eKeyHandled;
     }
 
@@ -2121,13 +2171,31 @@
     }
 
     m_selection_index++;
+    SkipNextHiddenFields();
 
-    FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index);
-    next_field->FieldDelegateSelectFirstElement();
+    if (m_selection_type == SelectionType::Field) {
+      FieldDelegateSP &next_field = m_delegate_sp->GetField(m_selection_index);
+      next_field->FieldDelegateSelectFirstElement();
+    }
 
     return eKeyHandled;
   }
 
+  void SkipPreviousHiddenFields() {
+    while (true) {
+      if (m_delegate_sp->GetField(m_selection_index)->FieldDelegateIsVisible())
+        return;
+
+      if (m_selection_index == 0) {
+        m_selection_type = SelectionType::Action;
+        m_selection_index = 0;
+        return;
+      }
+
+      m_selection_index--;
+    }
+  }
+
   HandleCharResult SelectePrevious(int key) {
     if (m_selection_type == SelectionType::Action) {
       if (m_selection_index > 0) {
@@ -2136,9 +2204,12 @@
       }
       m_selection_index = m_delegate_sp->GetNumberOfFields() - 1;
       m_selection_type = SelectionType::Field;
-      FieldDelegateSP &previous_field =
-          m_delegate_sp->GetField(m_selection_index);
-      previous_field->FieldDelegateSelectLastElement();
+      SkipPreviousHiddenFields();
+      if (m_selection_type == SelectionType::Field) {
+        FieldDelegateSP &previous_field =
+            m_delegate_sp->GetField(m_selection_index);
+        previous_field->FieldDelegateSelectLastElement();
+      }
       return eKeyHandled;
     }
 
@@ -2156,10 +2227,13 @@
     }
 
     m_selection_index--;
+    SkipPreviousHiddenFields();
 
-    FieldDelegateSP &previous_field =
-        m_delegate_sp->GetField(m_selection_index);
-    previous_field->FieldDelegateSelectLastElement();
+    if (m_selection_type == SelectionType::Field) {
+      FieldDelegateSP &previous_field =
+          m_delegate_sp->GetField(m_selection_index);
+      previous_field->FieldDelegateSelectLastElement();
+    }
 
     return eKeyHandled;
   }
@@ -2213,6 +2287,154 @@
   int m_first_visible_line;
 };
 
+///////////////////////////
+// Form Delegate Instances
+///////////////////////////
+
+class ProcessAttachFormDelegate : public FormDelegate {
+public:
+  ProcessAttachFormDelegate(Debugger &debugger) : m_debugger(debugger) {
+    std::vector<std::string> types;
+    types.push_back(std::string("Name"));
+    types.push_back(std::string("PID"));
+    m_type_field = AddChoicesField("Attach By", 2, types);
+
+    m_pid_field = AddIntegerField("PID", 0);
+
+    m_name_field = AddTextField("Process Name", "");
+
+    m_plugin_field = AddTextField("Plugin Name", "");
+
+    m_continue_field = AddBooleanField("Continue once attached.", false);
+
+    m_wait_for_field = AddBooleanField("Wait for process to launch.", false);
+    m_include_existing_field =
+        AddBooleanField("Include existing processes.", false);
+
+    AddAction("Attach", [this](Window &window) { Attach(window); });
+  }
+
+  std::string GetName() override { return "Attach Process"; }
+
+  void UpdateFieldsVisibility() override {
+    if (m_type_field->GetChoiceContent() == "Name") {
+      m_pid_field->FieldDelegateHide();
+      m_name_field->FieldDelegateShow();
+      m_wait_for_field->FieldDelegateShow();
+      if (m_wait_for_field->GetBoolean())
+        m_include_existing_field->FieldDelegateShow();
+      else
+        m_include_existing_field->FieldDelegateHide();
+    } else {
+      m_pid_field->FieldDelegateShow();
+      m_name_field->FieldDelegateHide();
+      m_wait_for_field->FieldDelegateHide();
+      m_include_existing_field->FieldDelegateHide();
+    }
+  }
+
+  void StopProcessIfNecessary() {
+    ExecutionContext exe_ctx =
+        m_debugger.GetCommandInterpreter().GetExecutionContext();
+
+    if (!exe_ctx.HasProcessScope())
+      return;
+
+    Process *process = exe_ctx.GetProcessPtr();
+    if (!(process && process->IsAlive()))
+      return;
+
+    if (process->GetShouldDetach()) {
+      Status detach_status(process->Detach(false));
+      if (detach_status.Fail())
+        SetError("Failed to detach from existing process.");
+    } else {
+      Status destroy_status(process->Destroy(false));
+      if (destroy_status.Fail())
+        SetError("Failed to kill exising process.");
+    }
+  }
+
+  Target *GetTarget() {
+    Target *target = m_debugger.GetSelectedTarget().get();
+
+    if (target != nullptr)
+      return target;
+
+    TargetSP new_target_sp;
+    m_debugger.GetTargetList().CreateTarget(
+        m_debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
+
+    target = new_target_sp.get();
+
+    if (target == nullptr)
+      SetError("Failed to create target.");
+
+    return target;
+  }
+
+  ProcessAttachInfo GetAttachInfo() {
+    ProcessAttachInfo attach_info;
+    attach_info.SetContinueOnceAttached(m_continue_field->GetBoolean());
+    if (m_type_field->GetChoiceContent() == "Name") {
+      attach_info.GetExecutableFile().SetFile(m_name_field->GetText(),
+                                              FileSpec::Style::native);
+      attach_info.SetWaitForLaunch(m_wait_for_field->GetBoolean());
+      if (m_wait_for_field->GetBoolean())
+        attach_info.SetIgnoreExisting(!m_include_existing_field->GetBoolean());
+    } else {
+      attach_info.SetProcessID(m_pid_field->GetInteger());
+    }
+    if (!m_plugin_field->GetText().empty())
+      attach_info.SetProcessPluginName(m_plugin_field->GetText());
+
+    return attach_info;
+  }
+
+  void Attach(Window &window) {
+    ClearError();
+
+    StopProcessIfNecessary();
+    if (HasError())
+      return;
+
+    Target *target = GetTarget();
+    if (HasError())
+      return;
+
+    StreamString stream;
+    ProcessAttachInfo attach_info = GetAttachInfo();
+    Status status = target->Attach(attach_info, &stream);
+
+    if (status.Fail()) {
+      SetError(status.AsCString());
+      return;
+    }
+
+    ProcessSP process_sp(target->GetProcessSP());
+    if (!process_sp) {
+      SetError("Attached sucessfully but target has no process.");
+      return;
+    }
+
+    if (attach_info.GetContinueOnceAttached())
+      process_sp->Resume();
+
+    window.GetParent()->RemoveSubWindow(&window);
+  }
+
+protected:
+  Debugger &m_debugger;
+
+  ChoicesFieldDelegate *m_type_field;
+  IntegerFieldDelegate *m_pid_field;
+  TextFieldDelegate *m_name_field;
+  TextFieldDelegate *m_plugin_field;
+  BooleanFieldDelegate *m_continue_field;
+  BooleanFieldDelegate *m_wait_for_field;
+  BooleanFieldDelegate *m_include_existing_field;
+};
+
 class MenuDelegate {
 public:
   virtual ~MenuDelegate() = default;
@@ -4462,6 +4684,19 @@
     }
       return MenuActionResult::Handled;
 
+    case eMenuID_ProcessAttach: {
+      WindowSP main_window_sp = m_app.GetMainWindow();
+      FormDelegateSP form_delegate_sp =
+          FormDelegateSP(new ProcessAttachFormDelegate(m_debugger));
+      Rect bounds = main_window_sp->GetCenteredRect(80, 19);
+      WindowSP form_window_sp = main_window_sp->CreateSubWindow(
+          form_delegate_sp->GetName().c_str(), bounds, true);
+      WindowDelegateSP window_delegate_sp =
+          WindowDelegateSP(new FormWindowDelegate(form_delegate_sp));
+      form_window_sp->SetDelegate(window_delegate_sp);
+      return MenuActionResult::Handled;
+    }
+
     case eMenuID_ProcessContinue: {
       ExecutionContext exe_ctx =
           m_debugger.GetCommandInterpreter().GetExecutionContext();
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to