public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Tom Tromey <tromey@adacore.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tromey@adacore.com>
Subject: [PATCH 2/2] Implement 'task apply'
Date: Thu,  4 Nov 2021 11:37:51 -0600	[thread overview]
Message-ID: <20211104173751.3029898-3-tromey@adacore.com> (raw)
In-Reply-To: <20211104173751.3029898-1-tromey@adacore.com>

This adds a 'task apply' command, which is the Ada tasking analogue of
'thread apply'.  Unlike 'thread apply', it doesn't offer the
'ascending' flag; but otherwise it's essentially the same.
---
 gdb/NEWS                           |   3 +
 gdb/ada-tasks.c                    | 204 ++++++++++++++++++++++++++++-
 gdb/doc/gdb.texinfo                |  35 +++++
 gdb/gdbthread.h                    |  17 +++
 gdb/testsuite/gdb.ada/rdv_wait.exp |   5 +
 gdb/thread.c                       |  31 ++---
 6 files changed, 277 insertions(+), 18 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index b6331d5585f..3ae65b17f07 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -24,6 +24,9 @@ show varsize-limit
   These are now deprecated aliases for "set max-value-size" and
   "show max-value-size".
 
+task apply [all | TASK-IDS...] [FLAG]... COMMAND
+  Like "thread apply", but applies COMMAND to Ada tasks.
+
 watch [...] task ID
   Watchpoints can now be restricted to a specific Ada task.
 
diff --git a/gdb/ada-tasks.c b/gdb/ada-tasks.c
index 32fbc14f703..d3ff1e2df64 100644
--- a/gdb/ada-tasks.c
+++ b/gdb/ada-tasks.c
@@ -1472,6 +1472,163 @@ ada_tasks_new_objfile_observer (struct objfile *objfile)
       ada_tasks_invalidate_inferior_data (inf);
 }
 
+/* The qcs command line flags for the "task apply" commands.  Keep
+   this in sync with the "frame apply" commands.  */
+
+using qcs_flag_option_def
+  = gdb::option::flag_option_def<qcs_flags>;
+
+static const gdb::option::option_def task_qcs_flags_option_defs[] = {
+  qcs_flag_option_def {
+    "q", [] (qcs_flags *opt) { return &opt->quiet; },
+    N_("Disables printing the task information."),
+  },
+
+  qcs_flag_option_def {
+    "c", [] (qcs_flags *opt) { return &opt->cont; },
+    N_("Print any error raised by COMMAND and continue."),
+  },
+
+  qcs_flag_option_def {
+    "s", [] (qcs_flags *opt) { return &opt->silent; },
+    N_("Silently ignore any errors or empty output produced by COMMAND."),
+  },
+};
+
+/* Create an option_def_group for the "task apply all" options, with
+   FLAGS as context.  */
+
+static inline std::array<gdb::option::option_def_group, 1>
+make_task_apply_all_options_def_group (qcs_flags *flags)
+{
+  return {{
+    { {task_qcs_flags_option_defs}, flags },
+  }};
+}
+
+/* Create an option_def_group for the "task apply" options, with
+   FLAGS as context.  */
+
+static inline gdb::option::option_def_group
+make_task_apply_options_def_group (qcs_flags *flags)
+{
+  return {{task_qcs_flags_option_defs}, flags};
+}
+
+/* Implementation of 'task apply all'.  */
+
+static void
+task_apply_all_command (const char *cmd, int from_tty)
+{
+  qcs_flags flags;
+
+  auto group = make_task_apply_all_options_def_group (&flags);
+  gdb::option::process_options
+    (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
+
+  validate_flags_qcs ("task apply all", &flags);
+
+  if (cmd == nullptr || *cmd == '\0')
+    error (_("Please specify a command at the end of 'task apply all'"));
+
+  update_thread_list ();
+  ada_build_task_list ();
+
+  inferior *inf = current_inferior ();
+  struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf);
+
+  /* Save a copy of the thread list and increment each thread's
+     refcount while executing the command in the context of each
+     thread, in case the command affects this.  */
+  std::vector<std::pair<int, thread_info_ref>> thr_list_cpy;
+
+  for (int i = 1; i <= data->task_list.size (); ++i)
+    {
+      ada_task_info &task = data->task_list[i - 1];
+      if (!ada_task_is_alive (&task))
+	continue;
+
+      thread_info *tp = find_thread_ptid (inf, task.ptid);
+      if (tp == nullptr)
+	warning (_("Unable to compute thread ID for task %s.\n"
+		   "Cannot switch to this task."),
+		 task_to_str (i, &task).c_str ());
+      else
+	thr_list_cpy.emplace_back (i, thread_info_ref::new_reference (tp));
+    }
+
+  scoped_restore_current_thread restore_thread;
+
+  for (const auto &info : thr_list_cpy)
+    if (switch_to_thread_if_alive (info.second.get ()))
+      thread_try_catch_cmd (info.second.get (), info.first, cmd,
+			    from_tty, flags);
+}
+
+/* Implementation of 'task apply'.  */
+
+static void
+task_apply_command (const char *tidlist, int from_tty)
+{
+
+  if (tidlist == nullptr || *tidlist == '\0')
+    error (_("Please specify a task ID list"));
+
+  update_thread_list ();
+  ada_build_task_list ();
+
+  inferior *inf = current_inferior ();
+  struct ada_tasks_inferior_data *data = get_ada_tasks_inferior_data (inf);
+
+  /* Save a copy of the thread list and increment each thread's
+     refcount while executing the command in the context of each
+     thread, in case the command affects this.  */
+  std::vector<std::pair<int, thread_info_ref>> thr_list_cpy;
+
+  number_or_range_parser parser (tidlist);
+  while (!parser.finished ())
+    {
+      int num = parser.get_number ();
+
+      if (num < 1 || num - 1 >= data->task_list.size ())
+	warning (_("no Ada Task with number %d"), num);
+      else
+	{
+	  ada_task_info &task = data->task_list[num - 1];
+	  if (!ada_task_is_alive (&task))
+	    continue;
+
+	  thread_info *tp = find_thread_ptid (inf, task.ptid);
+	  if (tp == nullptr)
+	    warning (_("Unable to compute thread ID for task %s.\n"
+		       "Cannot switch to this task."),
+		     task_to_str (num, &task).c_str ());
+	  else
+	    thr_list_cpy.emplace_back (num,
+				       thread_info_ref::new_reference (tp));
+	}
+    }
+
+  qcs_flags flags;
+  const char *cmd = parser.cur_tok ();
+
+  auto group = make_task_apply_options_def_group (&flags);
+  gdb::option::process_options
+    (&cmd, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, group);
+
+  validate_flags_qcs ("task apply", &flags);
+
+  if (*cmd == '\0')
+    error (_("Please specify a command following the task ID list"));
+
+  scoped_restore_current_thread restore_thread;
+
+  for (const auto &info : thr_list_cpy)
+    if (switch_to_thread_if_alive (info.second.get ()))
+      thread_try_catch_cmd (info.second.get (), info.first, cmd,
+			    from_tty, flags);
+}
+
 void _initialize_tasks ();
 void
 _initialize_tasks ()
@@ -1482,11 +1639,52 @@ _initialize_tasks ()
   gdb::observers::new_objfile.attach (ada_tasks_new_objfile_observer,
 				      "ada-tasks");
 
+  static struct cmd_list_element *task_cmd_list;
+  static struct cmd_list_element *task_apply_list;
+
+
   /* Some new commands provided by this module.  */
   add_info ("tasks", info_tasks_command,
 	    _("Provide information about all known Ada tasks."));
-  add_cmd ("task", class_run, task_command,
-	   _("Use this command to switch between Ada tasks.\n\
+
+  add_prefix_cmd ("task", class_run, task_command,
+		  _("Use this command to switch between Ada tasks.\n\
 Without argument, this command simply prints the current task ID."),
-	   &cmdlist);
+		  &task_cmd_list, 1, &cmdlist);
+
+#define TASK_APPLY_OPTION_HELP "\
+Prints per-inferior task number followed by COMMAND output.\n\
+\n\
+By default, an error raised during the execution of COMMAND\n\
+aborts \"task apply\".\n\
+\n\
+Options:\n\
+%OPTIONS%"
+
+  static const auto task_apply_opts
+    = make_task_apply_options_def_group (nullptr);
+
+  static std::string task_apply_help = gdb::option::build_help (_("\
+Apply a command to a list of tasks.\n\
+Usage: task apply ID... [OPTION]... COMMAND\n\
+ID is a space-separated list of IDs of tasks to apply COMMAND on.\n"
+TASK_APPLY_OPTION_HELP), task_apply_opts);
+
+  add_prefix_cmd ("apply", class_run,
+		  task_apply_command,
+		  task_apply_help.c_str (),
+		  &task_apply_list, 1,
+		  &task_cmd_list);
+
+  static const auto task_apply_all_opts
+    = make_task_apply_all_options_def_group (nullptr);
+
+  static std::string task_apply_all_help = gdb::option::build_help (_("\
+Apply a command to all tasks in the current inferior.\n\
+\n\
+Usage: task apply all [OPTION]... COMMAND\n"
+TASK_APPLY_OPTION_HELP), task_apply_all_opts);
+
+  add_cmd ("all", class_run, task_apply_all_command,
+	   task_apply_all_help.c_str (), &task_apply_list);
 }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b2bb079785e..5aad4436cb7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18539,6 +18539,41 @@ from the current task to the given task.
 #4  0x804aacc in un () at un.adb:5
 @end smallexample
 
+@item task apply [@var{task-id-list} | all] [@var{flag}]@dots{} @var{command}
+The @code{task apply} command is the Ada tasking analogue of
+@code{thread apply} (@pxref{Threads}).  It allows you to apply the
+named @var{command} to one or more tasks.  Specify the tasks that you
+want affected using a list of task IDs, or specify @code{all} to apply
+to all tasks.
+
+The @var{flag} arguments control what output to produce and how to
+handle errors raised when applying @var{command} to a task.
+@var{flag} must start with a @code{-} directly followed by one letter
+in @code{qcs}.  If several flags are provided, they must be given
+individually, such as @code{-c -q}.
+
+By default, @value{GDBN} displays some task information before the
+output produced by @var{command}, and an error raised during the
+execution of a @var{command} will abort @code{task apply}.  The
+following flags can be used to fine-tune this behavior:
+
+@table @code
+@item -c
+The flag @code{-c}, which stands for @samp{continue}, causes any
+errors in @var{command} to be displayed, and the execution of
+@code{task apply} then continues.
+@item -s
+The flag @code{-s}, which stands for @samp{silent}, causes any errors
+or empty output produced by a @var{command} to be silently ignored.
+That is, the execution continues, but the task information and errors
+are not printed.
+@item -q
+The flag @code{-q} (@samp{quiet}) disables printing the task
+information.
+@end table
+
+Flags @code{-c} and @code{-s} cannot be used together.
+
 @item break @var{location} task @var{taskno}
 @itemx break @var{location} task @var{taskno} if @dots{}
 @cindex breakpoints and tasks, in Ada
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index bd8d1a2952f..2fbf8f92cb9 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -975,4 +975,21 @@ extern void thread_select (const char *tidstr, class thread_info *thr);
    target to get the name.  May return nullptr.  */
 extern const char *thread_name (thread_info *thread);
 
+/* Switch to thread TP if it is alive.  Returns true if successfully
+   switched, false otherwise.  */
+
+extern bool switch_to_thread_if_alive (thread_info *thr);
+
+/* Assuming that THR is the current thread, execute CMD.
+   If ADA_TASK is not empty, it is the Ada task ID, and will
+   be printed instead of the thread information.
+   FLAGS.QUIET controls the printing of the thread information.
+   FLAGS.CONT and FLAGS.SILENT control how to handle errors.  Can throw an
+   exception if !FLAGS.SILENT and !FLAGS.CONT and CMD fails.  */
+
+extern void thread_try_catch_cmd (thread_info *thr,
+				  gdb::optional<int> ada_task,
+				  const char *cmd, int from_tty,
+				  const qcs_flags &flags);
+
 #endif /* GDBTHREAD_H */
diff --git a/gdb/testsuite/gdb.ada/rdv_wait.exp b/gdb/testsuite/gdb.ada/rdv_wait.exp
index 602443567a7..821adef4b1a 100644
--- a/gdb/testsuite/gdb.ada/rdv_wait.exp
+++ b/gdb/testsuite/gdb.ada/rdv_wait.exp
@@ -35,3 +35,8 @@ runto "break_me"
 gdb_test "task 2" \
          [join {"\\\[Switching to task 2 \"mit\"\\\].*" \
                 ".*foo\\.t \\(.*\\).*foo\\.adb:.*"} ""]
+
+gdb_test "task apply 1 -q frame" ".*pck\\.break_me.*"
+
+gdb_test "task apply all frame" \
+    "Task ID 1:.*pck\\.break_me.*Task ID 2:.*"
diff --git a/gdb/thread.c b/gdb/thread.c
index ee9f05325cd..6c792eccb8f 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -662,10 +662,9 @@ thread_alive (thread_info *tp)
   return target_thread_alive (tp->ptid);
 }
 
-/* Switch to thread TP if it is alive.  Returns true if successfully
-   switched, false otherwise.  */
+/* See gdbthreads.h.  */
 
-static bool
+bool
 switch_to_thread_if_alive (thread_info *thr)
 {
   scoped_restore_current_thread restore_thread;
@@ -1428,23 +1427,25 @@ tp_array_compar_descending (const thread_info_ref &a, const thread_info_ref &b)
   return (a->per_inf_num > b->per_inf_num);
 }
 
-/* Assuming that THR is the current thread, execute CMD.
-   FLAGS.QUIET controls the printing of the thread information.
-   FLAGS.CONT and FLAGS.SILENT control how to handle errors.  Can throw an
-   exception if !FLAGS.SILENT and !FLAGS.CONT and CMD fails.  */
+/* See gdbthread.h.  */
 
-static void
-thr_try_catch_cmd (thread_info *thr, const char *cmd, int from_tty,
-		   const qcs_flags &flags)
+void
+thread_try_catch_cmd (thread_info *thr, gdb::optional<int> ada_task,
+		      const char *cmd, int from_tty,
+		      const qcs_flags &flags)
 {
   gdb_assert (is_current_thread (thr));
 
   /* The thread header is computed before running the command since
      the command can change the inferior, which is not permitted
      by thread_target_id_str.  */
-  std::string thr_header =
-    string_printf (_("\nThread %s (%s):\n"), print_thread_id (thr),
-		   thread_target_id_str (thr).c_str ());
+  std::string thr_header;
+  if (ada_task.has_value ())
+    thr_header = string_printf (_("\nTask ID %d:\n"), *ada_task);
+  else
+    thr_header = string_printf (_("\nThread %s (%s):\n"),
+				print_thread_id (thr),
+				thread_target_id_str (thr).c_str ());
 
   try
     {
@@ -1576,7 +1577,7 @@ thread_apply_all_command (const char *cmd, int from_tty)
 
       for (thread_info_ref &thr : thr_list_cpy)
 	if (switch_to_thread_if_alive (thr.get ()))
-	  thr_try_catch_cmd (thr.get (), cmd, from_tty, flags);
+	  thread_try_catch_cmd (thr.get (), {}, cmd, from_tty, flags);
     }
 }
 
@@ -1738,7 +1739,7 @@ thread_apply_command (const char *tidlist, int from_tty)
 	  continue;
 	}
 
-      thr_try_catch_cmd (tp, cmd, from_tty, flags);
+      thread_try_catch_cmd (tp, {}, cmd, from_tty, flags);
     }
 }
 
-- 
2.31.1


  parent reply	other threads:[~2021-11-04 17:37 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-11-04 17:37 [PATCH 0/2] Two Ada task improvements Tom Tromey
2021-11-04 17:37 ` [PATCH 1/2] Add "task" keyword to the "watch" command Tom Tromey
2021-11-04 18:48   ` Eli Zaretskii
2021-11-04 17:37 ` Tom Tromey [this message]
2021-11-04 18:52   ` [PATCH 2/2] Implement 'task apply' Eli Zaretskii
2021-12-02 15:57 ` [PATCH 0/2] Two Ada task improvements Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211104173751.3029898-3-tromey@adacore.com \
    --to=tromey@adacore.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).