public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Simplify DAP stop reason emission
@ 2023-11-14 18:44 Tom Tromey
  2023-11-14 18:44 ` [PATCH 1/3] Move py_ui_out to a new header Tom Tromey
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Tom Tromey @ 2023-11-14 18:44 UTC (permalink / raw)
  To: gdb-patches

This patch changes the DAP stop-reason emission to be simpler (on the
DAP side) and also more reliable.  Rather than guessing that a stop
occurred because a "step" finished, the DAP code now relies on gdb's
own knowledge of this.

---
Tom Tromey (3):
      Move py_ui_out to a new header
      Emit stop reason details in Python stop events
      Simplify DAP stop-reason code

 gdb/NEWS                         |   4 +
 gdb/doc/python.texi              |  11 +++
 gdb/python/lib/gdb/dap/events.py |  79 ++++++++++++--------
 gdb/python/lib/gdb/dap/launch.py |   4 +-
 gdb/python/lib/gdb/dap/next.py   |  10 +--
 gdb/python/lib/gdb/dap/pause.py  |   4 +-
 gdb/python/py-bpevent.c          |   5 +-
 gdb/python/py-mi.c               | 131 +--------------------------------
 gdb/python/py-signalevent.c      |   5 +-
 gdb/python/py-stopevent.c        |  65 +++++++++++++++--
 gdb/python/py-stopevent.h        |   9 ++-
 gdb/python/py-uiout.h            | 153 +++++++++++++++++++++++++++++++++++++++
 12 files changed, 299 insertions(+), 181 deletions(-)
---
base-commit: c748dbd95bf3305539956053c2a25ecea7b442a5
change-id: 20231114-py-stop-reason-3f141487d90e

Best regards,
-- 
Tom Tromey <tromey@adacore.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] Move py_ui_out to a new header
  2023-11-14 18:44 [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
@ 2023-11-14 18:44 ` Tom Tromey
  2023-11-14 18:44 ` [PATCH 2/3] Emit stop reason details in Python stop events Tom Tromey
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2023-11-14 18:44 UTC (permalink / raw)
  To: gdb-patches

This moves the declaration of py_ui_out to a new header, so that it
can more readily be used by other code.
---
 gdb/python/py-mi.c    | 129 +-----------------------------------------
 gdb/python/py-uiout.h | 153 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 128 deletions(-)

diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index a7b4f4fa3cf..2b265ad80d6 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -19,142 +19,15 @@
 
 #include "defs.h"
 #include "python-internal.h"
+#include "py-uiout.h"
 #include "utils.h"
 #include "ui.h"
-#include "ui-out.h"
 #include "interps.h"
 #include "target.h"
 #include "mi/mi-parse.h"
 #include "mi/mi-console.h"
 #include "mi/mi-interp.h"
 
-/* A ui_out subclass that creates a Python object based on the data
-   that is passed in.  */
-
-class py_ui_out : public ui_out
-{
-public:
-
-  py_ui_out ()
-    : ui_out (fix_multi_location_breakpoint_output
-	      | fix_breakpoint_script_output)
-  {
-    do_begin (ui_out_type_tuple, nullptr);
-  }
-
-  bool can_emit_style_escape () const override
-  { return false; }
-
-  bool do_is_mi_like_p () const override
-  { return true; }
-
-  /* Return the Python object that was created.  If a Python error
-     occurred during the processing, set the Python error and return
-     nullptr.  */
-  PyObject *result ()
-  {
-    if (m_error.has_value ())
-      {
-	m_error->restore ();
-	return nullptr;
-      }
-    return current ().obj.release ();
-  }
-
-protected:
-
-  void do_progress_end () override { }
-  void do_progress_start () override { }
-  void do_progress_notify (const std::string &, const char *, double, double)
-    override
-  { }
-
-  void do_table_begin (int nbrofcols, int nr_rows, const char *tblid) override
-  {
-    do_begin (ui_out_type_list, tblid);
-  }
-  void do_table_body () override
-  { }
-  void do_table_end () override
-  {
-    do_end (ui_out_type_list);
-  }
-  void do_table_header (int width, ui_align align,
-			const std::string &col_name,
-			const std::string &col_hdr) override
-  { }
-
-  void do_begin (ui_out_type type, const char *id) override;
-  void do_end (ui_out_type type) override;
-
-  void do_field_signed (int fldno, int width, ui_align align,
-			const char *fldname, LONGEST value) override;
-  void do_field_unsigned (int fldno, int width, ui_align align,
-			  const char *fldname, ULONGEST value) override;
-
-  void do_field_skip (int fldno, int width, ui_align align,
-		      const char *fldname) override
-  { }
-
-  void do_field_string (int fldno, int width, ui_align align,
-			const char *fldname, const char *string,
-			const ui_file_style &style) override;
-  void do_field_fmt (int fldno, int width, ui_align align,
-		     const char *fldname, const ui_file_style &style,
-		     const char *format, va_list args) override
-    ATTRIBUTE_PRINTF (7, 0);
-
-  void do_spaces (int numspaces) override
-  { }
-
-  void do_text (const char *string) override
-  { }
-
-  void do_message (const ui_file_style &style,
-		   const char *format, va_list args)
-    override ATTRIBUTE_PRINTF (3,0)
-  { }
-
-  void do_wrap_hint (int indent) override
-  { }
-
-  void do_flush () override
-  { }
-
-  void do_redirect (struct ui_file *outstream) override
-  { }
-
-private:
-
-  /* When constructing Python objects, this class keeps a stack of
-     objects being constructed.  Each such object has this type.  */
-  struct object_desc
-  {
-    /* Name of the field (or empty for lists) that this object will
-       eventually become.  */
-    std::string field_name;
-    /* The object under construction.  */
-    gdbpy_ref<> obj;
-    /* The type of structure being created.  Note that tables are
-       treated as lists here.  */
-    ui_out_type type;
-  };
-
-  /* The stack of objects being created.  */
-  std::vector<object_desc> m_objects;
-
-  /* If an error occurred, this holds the exception information for
-     use by the 'release' method.  */
-  gdb::optional<gdbpy_err_fetch> m_error;
-
-  /* Return a reference to the object under construction.  */
-  object_desc &current ()
-  { return m_objects.back (); }
-
-  /* Add a new field to the current object under construction.  */
-  void add_field (const char *name, const gdbpy_ref<> &obj);
-};
-
 void
 py_ui_out::add_field (const char *name, const gdbpy_ref<> &obj)
 {
diff --git a/gdb/python/py-uiout.h b/gdb/python/py-uiout.h
new file mode 100644
index 00000000000..e9abf8ee5be
--- /dev/null
+++ b/gdb/python/py-uiout.h
@@ -0,0 +1,153 @@
+/* Python implementation of ui_out
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDB_PYTHON_PY_UIOUT_H
+#define GDB_PYTHON_PY_UIOUT_H
+
+#include "python-internal.h"
+#include "ui-out.h"
+
+/* A ui_out subclass that creates a Python object based on the data
+   that is passed in.  */
+
+class py_ui_out : public ui_out
+{
+public:
+
+  py_ui_out ()
+    : ui_out (fix_multi_location_breakpoint_output
+	      | fix_breakpoint_script_output)
+  {
+    do_begin (ui_out_type_tuple, nullptr);
+  }
+
+  bool can_emit_style_escape () const override
+  { return false; }
+
+  bool do_is_mi_like_p () const override
+  { return true; }
+
+  /* Return the Python object that was created.  If a Python error
+     occurred during the processing, set the Python error and return
+     nullptr.  */
+  PyObject *result ()
+  {
+    if (m_error.has_value ())
+      {
+	m_error->restore ();
+	return nullptr;
+      }
+    return current ().obj.release ();
+  }
+
+protected:
+
+  void do_progress_end () override { }
+  void do_progress_start () override { }
+  void do_progress_notify (const std::string &, const char *, double, double)
+    override
+  { }
+
+  void do_table_begin (int nbrofcols, int nr_rows, const char *tblid) override
+  {
+    do_begin (ui_out_type_list, tblid);
+  }
+  void do_table_body () override
+  { }
+  void do_table_end () override
+  {
+    do_end (ui_out_type_list);
+  }
+  void do_table_header (int width, ui_align align,
+			const std::string &col_name,
+			const std::string &col_hdr) override
+  { }
+
+  void do_begin (ui_out_type type, const char *id) override;
+  void do_end (ui_out_type type) override;
+
+  void do_field_signed (int fldno, int width, ui_align align,
+			const char *fldname, LONGEST value) override;
+  void do_field_unsigned (int fldno, int width, ui_align align,
+			  const char *fldname, ULONGEST value) override;
+
+  void do_field_skip (int fldno, int width, ui_align align,
+		      const char *fldname) override
+  { }
+
+  void do_field_string (int fldno, int width, ui_align align,
+			const char *fldname, const char *string,
+			const ui_file_style &style) override;
+  void do_field_fmt (int fldno, int width, ui_align align,
+		     const char *fldname, const ui_file_style &style,
+		     const char *format, va_list args) override
+    ATTRIBUTE_PRINTF (7, 0);
+
+  void do_spaces (int numspaces) override
+  { }
+
+  void do_text (const char *string) override
+  { }
+
+  void do_message (const ui_file_style &style,
+		   const char *format, va_list args)
+    override ATTRIBUTE_PRINTF (3,0)
+  { }
+
+  void do_wrap_hint (int indent) override
+  { }
+
+  void do_flush () override
+  { }
+
+  void do_redirect (struct ui_file *outstream) override
+  { }
+
+private:
+
+  /* When constructing Python objects, this class keeps a stack of
+     objects being constructed.  Each such object has this type.  */
+  struct object_desc
+  {
+    /* Name of the field (or empty for lists) that this object will
+       eventually become.  */
+    std::string field_name;
+    /* The object under construction.  */
+    gdbpy_ref<> obj;
+    /* The type of structure being created.  Note that tables are
+       treated as lists here.  */
+    ui_out_type type;
+  };
+
+  /* The stack of objects being created.  */
+  std::vector<object_desc> m_objects;
+
+  /* If an error occurred, this holds the exception information for
+     use by the 'release' method.  */
+  gdb::optional<gdbpy_err_fetch> m_error;
+
+  /* Return a reference to the object under construction.  */
+  object_desc &current ()
+  { return m_objects.back (); }
+
+  /* Add a new field to the current object under construction.  */
+  void add_field (const char *name, const gdbpy_ref<> &obj);
+};
+
+#endif /* GDB_PYTHON_PY_UIOUT_H */

-- 
2.41.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/3] Emit stop reason details in Python stop events
  2023-11-14 18:44 [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
  2023-11-14 18:44 ` [PATCH 1/3] Move py_ui_out to a new header Tom Tromey
@ 2023-11-14 18:44 ` Tom Tromey
  2023-11-14 19:12   ` Eli Zaretskii
  2023-11-14 18:44 ` [PATCH 3/3] Simplify DAP stop-reason code Tom Tromey
  2023-12-11 18:42 ` [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
  3 siblings, 1 reply; 6+ messages in thread
From: Tom Tromey @ 2023-11-14 18:44 UTC (permalink / raw)
  To: gdb-patches

This changes Python stop events to carry a "details" dictionary, that
holds any relevant information about the stop.  The details are
constructed using more or less the same procedure as is done for MI.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=13587
---
 gdb/NEWS                    |  4 +++
 gdb/doc/python.texi         | 11 ++++++++
 gdb/python/py-bpevent.c     |  5 ++--
 gdb/python/py-mi.c          |  2 +-
 gdb/python/py-signalevent.c |  5 ++--
 gdb/python/py-stopevent.c   | 65 +++++++++++++++++++++++++++++++++++++++++----
 gdb/python/py-stopevent.h   |  9 ++++---
 gdb/python/py-uiout.h       |  4 +--
 8 files changed, 90 insertions(+), 15 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 93fbcf1c13e..12387ad9f4a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -50,6 +50,10 @@ disable missing-debug-handler LOCUS HANDLER
      sub-classed to create handlers for objfiles with missing debug
      information.
 
+  ** Stop events now have a "details" attribute that holds a
+     dictionary that carries the same information as an MI "*stopped"
+     event.
+
 * New commands
 
 maintenance info linux-lwps
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index b65991bbad0..ab2abcfc5b1 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -3750,6 +3750,17 @@ registry extend @code{gdb.StopEvent}.  As a child of
 thread when @value{GDBN} is running in non-stop mode.  Refer to
 @code{gdb.ThreadEvent} above for more details.
 
+@code{gdb.StopEvent} has the following additional attributes:
+
+@defvar StopEvent.details
+A dictionary holding any details relevant to the stop.  The exact keys
+and values depend on the type of stop, but are identical to the
+corresponding MI output (@pxref{GDB/MI Async Records}).
+
+A dictionary was used for this (rather than adding attributes directly
+to the event object) so that the MI keys could be used unchanged.
+@end defvar
+
 Emits @code{gdb.SignalEvent}, which extends @code{gdb.StopEvent}.
 
 This event indicates that the inferior or one of its threads has
diff --git a/gdb/python/py-bpevent.c b/gdb/python/py-bpevent.c
index 00fb625794b..b640ec9ddd9 100644
--- a/gdb/python/py-bpevent.c
+++ b/gdb/python/py-bpevent.c
@@ -24,10 +24,11 @@
    references to BREAKPOINT_LIST and FIRST_BP.  */
 
 gdbpy_ref<>
-create_breakpoint_event_object (PyObject *breakpoint_list, PyObject *first_bp)
+create_breakpoint_event_object (const gdbpy_ref<> &dict,
+				PyObject *breakpoint_list, PyObject *first_bp)
 {
   gdbpy_ref<> breakpoint_event_obj
-    = create_stop_event_object (&breakpoint_event_object_type);
+    = create_stop_event_object (&breakpoint_event_object_type, dict);
 
   if (breakpoint_event_obj == NULL)
     return NULL;
diff --git a/gdb/python/py-mi.c b/gdb/python/py-mi.c
index 2b265ad80d6..04873e86dd3 100644
--- a/gdb/python/py-mi.c
+++ b/gdb/python/py-mi.c
@@ -173,7 +173,7 @@ gdbpy_execute_mi_command (PyObject *self, PyObject *args, PyObject *kw)
       return nullptr;
     }
 
-  return uiout.result ();
+  return uiout.result ().release ();
 }
 
 /* Convert KEY_OBJ into a string that can be used as a field name in MI
diff --git a/gdb/python/py-signalevent.c b/gdb/python/py-signalevent.c
index 93d06706542..edbd9b0a7ec 100644
--- a/gdb/python/py-signalevent.c
+++ b/gdb/python/py-signalevent.c
@@ -21,10 +21,11 @@
 #include "py-stopevent.h"
 
 gdbpy_ref<>
-create_signal_event_object (enum gdb_signal stop_signal)
+create_signal_event_object (const gdbpy_ref<> &dict,
+			    enum gdb_signal stop_signal)
 {
   gdbpy_ref<> signal_event_obj
-    = create_stop_event_object (&signal_event_object_type);
+    = create_stop_event_object (&signal_event_object_type, dict);
 
   if (signal_event_obj == NULL)
     return NULL;
diff --git a/gdb/python/py-stopevent.c b/gdb/python/py-stopevent.c
index 0aa9d5381f8..f395e8c042e 100644
--- a/gdb/python/py-stopevent.c
+++ b/gdb/python/py-stopevent.c
@@ -19,12 +19,61 @@
 
 #include "defs.h"
 #include "py-stopevent.h"
+#include "py-uiout.h"
 
 gdbpy_ref<>
-create_stop_event_object (PyTypeObject *py_type)
+create_stop_event_object (PyTypeObject *py_type, const gdbpy_ref<> &dict)
 {
   gdbpy_ref<> thread = py_get_event_thread (inferior_ptid);
-  return create_thread_event_object (py_type, thread.get ());
+  if (thread == nullptr)
+    return nullptr;
+
+  gdbpy_ref<> result = create_thread_event_object (py_type, thread.get ());
+  if (result == nullptr)
+    return nullptr;
+
+  if (evpy_add_attribute (result.get (), "details", dict.get ()) < 0)
+    return nullptr;
+
+  return result;
+}
+
+/* Print BPSTAT to a new Python dictionary.  Returns the dictionary,
+   or null if a Python exception occurred.  */
+
+static gdbpy_ref<>
+py_print_bpstat (bpstat *bs, enum gdb_signal stop_signal)
+{
+  py_ui_out uiout;
+
+  try
+    {
+      scoped_restore save_uiout = make_scoped_restore (&current_uiout, &uiout);
+
+      thread_info *tp = inferior_thread ();
+      if (tp->thread_fsm () != nullptr && tp->thread_fsm ()->finished_p ())
+	{
+	  async_reply_reason reason = tp->thread_fsm ()->async_reply_reason ();
+	  uiout.field_string ("reason", async_reason_lookup (reason));
+	}
+
+      if (stop_signal != GDB_SIGNAL_0 && stop_signal != GDB_SIGNAL_TRAP)
+	print_signal_received_reason (&uiout, stop_signal);
+      else
+	{
+	  struct target_waitstatus last;
+	  get_last_target_status (nullptr, nullptr, &last);
+
+	  bpstat_print (bs, last.kind ());
+	}
+    }
+  catch (const gdb_exception &except)
+    {
+      gdbpy_convert_exception (except);
+      return nullptr;
+    }
+
+  return uiout.result ();
 }
 
 /* Callback observers when a stop event occurs.  This function will create a
@@ -45,6 +94,10 @@ emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal)
   if (evregpy_no_listeners_p (gdb_py_events.stop))
     return 0;
 
+  gdbpy_ref<> dict = py_print_bpstat (bs, stop_signal);
+  if (dict == nullptr)
+    return -1;
+
   /* Add any breakpoint set at this location to the list.  */
   for (current_bs = bs; current_bs != NULL; current_bs = current_bs->next)
     {
@@ -71,7 +124,8 @@ emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal)
 
   if (list != NULL)
     {
-      stop_event_obj = create_breakpoint_event_object (list.get (),
+      stop_event_obj = create_breakpoint_event_object (dict,
+						       list.get (),
 						       first_bp);
       if (stop_event_obj == NULL)
 	return -1;
@@ -81,7 +135,7 @@ emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal)
   if (stop_signal != GDB_SIGNAL_0
       && stop_signal != GDB_SIGNAL_TRAP)
     {
-      stop_event_obj = create_signal_event_object (stop_signal);
+      stop_event_obj = create_signal_event_object (dict, stop_signal);
       if (stop_event_obj == NULL)
 	return -1;
     }
@@ -90,7 +144,8 @@ emit_stop_event (struct bpstat *bs, enum gdb_signal stop_signal)
      be known and this should eventually be unused.  */
   if (stop_event_obj == NULL)
     {
-      stop_event_obj = create_stop_event_object (&stop_event_object_type);
+      stop_event_obj = create_stop_event_object (&stop_event_object_type,
+						 dict);
       if (stop_event_obj == NULL)
 	return -1;
     }
diff --git a/gdb/python/py-stopevent.h b/gdb/python/py-stopevent.h
index 649112f0488..92282c9c413 100644
--- a/gdb/python/py-stopevent.h
+++ b/gdb/python/py-stopevent.h
@@ -22,14 +22,17 @@
 
 #include "py-event.h"
 
-extern gdbpy_ref<> create_stop_event_object (PyTypeObject *py_type);
+extern gdbpy_ref<> create_stop_event_object (PyTypeObject *py_type,
+					     const gdbpy_ref<> &dict);
 
 extern int emit_stop_event (struct bpstat *bs,
 			    enum gdb_signal stop_signal);
 
-extern gdbpy_ref<> create_breakpoint_event_object (PyObject *breakpoint_list,
+extern gdbpy_ref<> create_breakpoint_event_object (const gdbpy_ref<> &dict,
+						   PyObject *breakpoint_list,
 						   PyObject *first_bp);
 
-extern gdbpy_ref<> create_signal_event_object (enum gdb_signal stop_signal);
+extern gdbpy_ref<> create_signal_event_object (const gdbpy_ref<> &dict,
+					       enum gdb_signal stop_signal);
 
 #endif /* PYTHON_PY_STOPEVENT_H */
diff --git a/gdb/python/py-uiout.h b/gdb/python/py-uiout.h
index e9abf8ee5be..6e43b425968 100644
--- a/gdb/python/py-uiout.h
+++ b/gdb/python/py-uiout.h
@@ -46,14 +46,14 @@ class py_ui_out : public ui_out
   /* Return the Python object that was created.  If a Python error
      occurred during the processing, set the Python error and return
      nullptr.  */
-  PyObject *result ()
+  gdbpy_ref<> result ()
   {
     if (m_error.has_value ())
       {
 	m_error->restore ();
 	return nullptr;
       }
-    return current ().obj.release ();
+    return std::move (current ().obj);
   }
 
 protected:

-- 
2.41.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/3] Simplify DAP stop-reason code
  2023-11-14 18:44 [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
  2023-11-14 18:44 ` [PATCH 1/3] Move py_ui_out to a new header Tom Tromey
  2023-11-14 18:44 ` [PATCH 2/3] Emit stop reason details in Python stop events Tom Tromey
@ 2023-11-14 18:44 ` Tom Tromey
  2023-12-11 18:42 ` [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
  3 siblings, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2023-11-14 18:44 UTC (permalink / raw)
  To: gdb-patches

Now that gdb adds stop-reason details to stop events, we can simplify
the DAP code to emit correct stop reasons in its own events.  For the
most part a simple renaming of gdb reasons is sufficient; however,
"pause" must still be handled specially.
---
 gdb/python/lib/gdb/dap/events.py | 79 +++++++++++++++++++++++++---------------
 gdb/python/lib/gdb/dap/launch.py |  4 +-
 gdb/python/lib/gdb/dap/next.py   | 10 ++---
 gdb/python/lib/gdb/dap/pause.py  |  4 +-
 4 files changed, 57 insertions(+), 40 deletions(-)

diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py
index e9ddcab135f..bdd2a6ebadc 100644
--- a/gdb/python/lib/gdb/dap/events.py
+++ b/gdb/python/lib/gdb/dap/events.py
@@ -99,60 +99,79 @@ def _cont(event):
         )
 
 
-class StopKinds(enum.Enum):
-    # The values here are chosen to follow the DAP spec.
-    STEP = "step"
-    BREAKPOINT = "breakpoint"
-    PAUSE = "pause"
-    EXCEPTION = "exception"
-
-
-_expected_stop = None
-
-
-@in_gdb_thread
-def expect_stop(reason):
-    """Indicate that a stop is expected, for the reason given."""
-    global _expected_stop
-    _expected_stop = reason
+_expected_pause = False
 
 
 # A wrapper for Invoker that also sets the expected stop.
 class ExecutionInvoker(Invoker):
-    """A subclass of Invoker that sets the expected stop.
-    Note that this assumes that the command will restart the inferior,
-    so it will also cause ContinuedEvents to be suppressed."""
+    """A subclass of Invoker that sets the continue-suppression flag.
 
-    def __init__(self, cmd, expected):
+    When EXPECTED_PAUSE is True, a stop that looks like a pause (e.g.,
+    a SIGINT) will be reported as "pause" instead.
+    """
+
+    def __init__(self, cmd, expected_pause=False):
         super().__init__(cmd)
-        self.expected = expected
+        self.expected_pause = expected_pause
 
     @in_gdb_thread
     def __call__(self):
-        expect_stop(self.expected)
+        global _expected_pause
+        _expected_pause = self.expected_pause
         global _suppress_cont
         _suppress_cont = True
         # FIXME if the call fails should we clear _suppress_cont?
         super().__call__()
 
 
+# Map from gdb stop reasons to DAP stop reasons.  Some of these can't
+# be seen ordinarily in DAP -- only if the client lets the user toggle
+# some settings (e.g. stop-on-solib-events) or enter commands (e.g.,
+# 'until').
+stop_reason_map = {
+    "breakpoint-hit": "breakpoint",
+    "watchpoint-trigger": "data breakpoint",
+    "read-watchpoint-trigger": "data breakpoint",
+    "access-watchpoint-trigger": "data breakpoint",
+    "function-finished": "step",
+    "location-reached": "step",
+    "watchpoint-scope": "data breakpoint",
+    "end-stepping-range": "step",
+    "exited-signalled": "exited",
+    "exited": "exited",
+    "exited-normally": "exited",
+    "signal-received": "signal",
+    "solib-event": "solib",
+    "fork": "fork",
+    "vfork": "vfork",
+    "syscall-entry": "syscall-entry",
+    "syscall-return": "syscall-return",
+    "exec": "exec",
+    "no-history": "no-history",
+}
+
+
 @in_gdb_thread
 def _on_stop(event):
     log("entering _on_stop: " + repr(event))
-    global _expected_stop
+    log("   details: " + repr(event.details))
     obj = {
         "threadId": gdb.selected_thread().global_num,
         "allThreadsStopped": True,
     }
     if isinstance(event, gdb.BreakpointEvent):
-        # Ignore the expected stop, we hit a breakpoint instead.
-        _expected_stop = StopKinds.BREAKPOINT
         obj["hitBreakpointIds"] = [x.number for x in event.breakpoints]
-    elif _expected_stop is None:
-        # FIXME what is even correct here
-        _expected_stop = StopKinds.EXCEPTION
-    obj["reason"] = _expected_stop.value
-    _expected_stop = None
+    global stop_reason_map
+    reason = event.details["reason"]
+    global _expected_pause
+    if (
+        _expected_pause
+        and reason == "signal-received"
+        and event.details["signal-name"] in ("SIGINT", "SIGSTOP")
+    ):
+        obj["reason"] = "pause"
+    else:
+        obj["reason"] = stop_reason_map[reason]
     send_event("stopped", obj)
 
 
diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
index d13037fa476..7dead0c7c35 100644
--- a/gdb/python/lib/gdb/dap/launch.py
+++ b/gdb/python/lib/gdb/dap/launch.py
@@ -88,6 +88,4 @@ def attach(*, pid: Optional[int] = None, target: Optional[str] = None, **args):
 def config_done(**args):
     global _program
     if _program is not None:
-        # Suppress the continue event, but don't set any particular
-        # expected stop.
-        send_gdb(ExecutionInvoker("run", None))
+        send_gdb(ExecutionInvoker("run"))
diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py
index e5bb8d64da0..76c9360383a 100644
--- a/gdb/python/lib/gdb/dap/next.py
+++ b/gdb/python/lib/gdb/dap/next.py
@@ -15,7 +15,7 @@
 
 import gdb
 
-from .events import StopKinds, ExecutionInvoker
+from .events import ExecutionInvoker
 from .server import capability, request
 from .startup import in_gdb_thread, send_gdb, send_gdb_with_response
 from .state import set_thread
@@ -57,7 +57,7 @@ def next(
     cmd = "next"
     if granularity == "instruction":
         cmd += "i"
-    send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
+    send_gdb(ExecutionInvoker(cmd))
 
 
 @capability("supportsSteppingGranularity")
@@ -70,17 +70,17 @@ def step_in(
     cmd = "step"
     if granularity == "instruction":
         cmd += "i"
-    send_gdb(ExecutionInvoker(cmd, StopKinds.STEP))
+    send_gdb(ExecutionInvoker(cmd))
 
 
 @request("stepOut")
 def step_out(*, threadId: int, singleThread: bool = False, **args):
     send_gdb(lambda: _handle_thread_step(threadId, singleThread, True))
-    send_gdb(ExecutionInvoker("finish", StopKinds.STEP))
+    send_gdb(ExecutionInvoker("finish"))
 
 
 @request("continue")
 def continue_request(*, threadId: int, singleThread: bool = False, **args):
     locked = send_gdb_with_response(lambda: _handle_thread_step(threadId, singleThread))
-    send_gdb(ExecutionInvoker("continue", None))
+    send_gdb(ExecutionInvoker("continue"))
     return {"allThreadsContinued": not locked}
diff --git a/gdb/python/lib/gdb/dap/pause.py b/gdb/python/lib/gdb/dap/pause.py
index 1e59d630523..a89741cbf69 100644
--- a/gdb/python/lib/gdb/dap/pause.py
+++ b/gdb/python/lib/gdb/dap/pause.py
@@ -13,11 +13,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from .events import StopKinds, ExecutionInvoker
+from .events import ExecutionInvoker
 from .server import request
 from .startup import send_gdb
 
 
 @request("pause")
 def pause(**args):
-    send_gdb(ExecutionInvoker("interrupt -a", StopKinds.PAUSE))
+    send_gdb(ExecutionInvoker("interrupt -a", True))

-- 
2.41.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] Emit stop reason details in Python stop events
  2023-11-14 18:44 ` [PATCH 2/3] Emit stop reason details in Python stop events Tom Tromey
@ 2023-11-14 19:12   ` Eli Zaretskii
  0 siblings, 0 replies; 6+ messages in thread
From: Eli Zaretskii @ 2023-11-14 19:12 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

> From: Tom Tromey <tromey@adacore.com>
> Date: Tue, 14 Nov 2023 11:44:04 -0700
> 
> This changes Python stop events to carry a "details" dictionary, that
> holds any relevant information about the stop.  The details are
> constructed using more or less the same procedure as is done for MI.
> 
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=13587
> ---
>  gdb/NEWS                    |  4 +++
>  gdb/doc/python.texi         | 11 ++++++++
>  gdb/python/py-bpevent.c     |  5 ++--
>  gdb/python/py-mi.c          |  2 +-
>  gdb/python/py-signalevent.c |  5 ++--
>  gdb/python/py-stopevent.c   | 65 +++++++++++++++++++++++++++++++++++++++++----
>  gdb/python/py-stopevent.h   |  9 ++++---
>  gdb/python/py-uiout.h       |  4 +--
>  8 files changed, 90 insertions(+), 15 deletions(-)

Thanks, the documentation parts are okay.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] Simplify DAP stop reason emission
  2023-11-14 18:44 [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
                   ` (2 preceding siblings ...)
  2023-11-14 18:44 ` [PATCH 3/3] Simplify DAP stop-reason code Tom Tromey
@ 2023-12-11 18:42 ` Tom Tromey
  3 siblings, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2023-12-11 18:42 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

>>>>> "Tom" == Tom Tromey <tromey@adacore.com> writes:

Tom> This patch changes the DAP stop-reason emission to be simpler (on the
Tom> DAP side) and also more reliable.  Rather than guessing that a stop
Tom> occurred because a "step" finished, the DAP code now relies on gdb's
Tom> own knowledge of this.

I rebased this, which required a bit of fixing in events.py.
I'm going to check it in now.

Tom

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2023-12-11 18:42 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-14 18:44 [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey
2023-11-14 18:44 ` [PATCH 1/3] Move py_ui_out to a new header Tom Tromey
2023-11-14 18:44 ` [PATCH 2/3] Emit stop reason details in Python stop events Tom Tromey
2023-11-14 19:12   ` Eli Zaretskii
2023-11-14 18:44 ` [PATCH 3/3] Simplify DAP stop-reason code Tom Tromey
2023-12-11 18:42 ` [PATCH 0/3] Simplify DAP stop reason emission Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).