[5/6] gdb: add commands to control scheduler locking.

Message ID 20231229104202.7878-6-natalia.saiapova@intel.com
State New
Headers
Series Refinement of scheduler-locking settings |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Testing failed

Commit Message

Natalia Saiapova Dec. 29, 2023, 10:42 a.m. UTC
  In this patch, we introduce new command options for set/show scheduler
locking.  New options give the user finer control over the scheduler.

Introduce
set scheduler-locking <step | run | replay step | replay run> <on | off>
show scheduler-locking <step | run | replay step | replay run>

For example, with these commands a user can get a combined scheduler locking
for stepping commands during the normal execution and for all commands in
replay mode.

The existing scheduler-locking settings still exist and work as
shortcuts.

  set scheduler-locking step
is equivalent to
  set scheduler-locking replay run off
  set scheduler-locking replay step on
  set scheduler-locking run off
  set scheduler-locking step on

  set scheduler-locking on
is equivalent to
  set scheduler-locking replay run on
  set scheduler-locking replay step on
  set scheduler-locking run on
  set scheduler-locking step on

  set scheduler-locking replay
is equivalent to
  set scheduler-locking replay run on
  set scheduler-locking replay step on
  set scheduler-locking run off
  set scheduler-locking step off

  set scheduler-locking off
is equivalent to
  set scheduler-locking replay run off
  set scheduler-locking replay step off
  set scheduler-locking run off
  set scheduler-locking step off

This is bound to the structure we introduced in the previous commit:
  gdb: change the internal representation of scheduler locking.

To introduce it under scheduler-locking I had to change the way the show
command works.

  (gdb) show scheduler-locking
  scheduler-locking replay run:  "on"     Scheduler locking for non-stepping commands is "on" during replay mode.
  scheduler-locking replay step:  "on"    Scheduler locking for stepping commands is "on" during replay mode.
  scheduler-locking run:  "off"   Scheduler locking for non-stepping commands is "off" during normal execution.
  scheduler-locking step:  "off"  Scheduler locking for stepping commands is "off" during normal execution.

  (gdb) show scheduler-locking replay
  scheduler-locking replay run:  "on"     Scheduler locking for non-stepping commands is "on" during replay mode.
  scheduler-locking replay step:  "on"    Scheduler locking for stepping commands is "on" during replay mode.

  (gdb) show scheduler-locking replay step
  "on"    Scheduler locking for stepping commands is "on" during replay mode.

  (gdb) show scheduler-locking run
  "off"   Scheduler locking for non-stepping commands is "off" during normal execution.

Note, there is a small inconsistency with the "set scheduler-locking
step".  If we did not keep the older way of setting the scheduler
locking, command
  set scheduler-locking step
would be the same as
  set scheduler-locking step on
while to be backward compatible, we have it as
  set scheduler-locking step on
  set scheduler-locking replay step on
---
 gdb/NEWS                                      |  17 ++
 gdb/doc/gdb.texinfo                           |  61 ++++-
 gdb/infrun.c                                  | 220 ++++++++++++++----
 .../gdb.mi/user-selected-context-sync.exp     |  20 +-
 .../gdb.threads/hand-call-in-threads.exp      |   8 +-
 .../multiple-successive-infcall.exp           |   5 +-
 gdb/testsuite/gdb.threads/schedlock.exp       |  79 ++++++-
 gdb/testsuite/lib/gdb.exp                     |  53 ++++-
 8 files changed, 367 insertions(+), 96 deletions(-)
  

Comments

Eli Zaretskii Dec. 29, 2023, 12:03 p.m. UTC | #1
> From: Natalia Saiapova <natalia.saiapova@intel.com>
> Cc: tankut.baris.aktemur@intel.com
> Date: Fri, 29 Dec 2023 10:42:01 +0000
> 
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -18,6 +18,23 @@ disassemble
>  
>  * New commands
>  
> +set scheduler-locking replay run | replay step | run | step (on|off)
> +show scheduler-locking (replay run | replay step | run | step)
> +  Extend the scheduler locking settings with a set of set/show
> +  commands, which can be used individually to control the scheduler during
> +  various commands.
> +    'replay run' -- when on, the scheduler is locked during non-stepping
> +    commands in replay mode.
> +    'replay step' -- when on, the scheduler is locked during stepping
> +    commands in replay mode.
> +    'run' -- when on, the scheduler is locked during non-stepping commands
> +    in normal mode.
> +    'step' -- when on, the scheduler is locked during stepping commands
> +    in normal mode.
> +  The older scheduler locking settings can be used as shortcuts, their behavior
> +  is not changed.
> +  The output of "show scheduler-locking" has changed to support the new settings.
> +

I suggest to mention a couple of examples of non-stepping commands
where these settings are relevant.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7110,22 +7110,56 @@ On some OSes, you can modify @value{GDBN}'s default behavior by
>  locking the OS scheduler to allow only a single thread to run.
>  
>  @table @code
> -@item set scheduler-locking @var{mode}
> -@cindex scheduler-locking
> +
> +@item set scheduler-locking @var{type} [@code{on}|@code{off}]
> +@cindex scheduler locking type
> +@cindex lock scheduler
> +Set the scheduler locking settings.  It applies to normal execution,
> +record mode, and replay mode.  The scheduler locking can be set separately
> +for stepping and non-stepping commands.

Similarly here.  In the manual, it is actually much more important,
since the manual is supposed to be the ultimate source of information
about GDB commands: after reading the relevant parts of the manual,
the user is supposed to understand the command enough to be able to
use it intelligently and correctly.

> +@table @code
> +@item replay run
> +When @code{on} the scheduler is locked for non-stepping commands during
                 ^
A comma is missing there.

> +replay mode.  For commands like @samp{continue}, @samp{until}, @samp{finish},
> +or expression evaluation only the current thread may run.

The last sentence should IMO be moved to where you say "non-stepping
commands" above, because it actually explains what are those commands.

Btw, I'm a bit worried about our use of "scheduler lock", with the
meaning of "prevent other threads from running".  We never say that
locking the scheduler means only the current thread is allowed to run,
we rely on the readers to figure that out on their own.  (Yes, I
understand that this was the problem with the original text as well.)

> +@item replay step
> +When @code{on} the scheduler is locked for stepping commands during replay
> +mode.  This mode optimizes for single-stepping; only the current thread is
> +resumed while you are stepping, so that the focus of debugging does not change
> +unexpectedly.
> +
> +@item run
> +When @code{on} the scheduler is locked for non-stepping commands during
> +normal execution and record modes.  For commands like @samp{continue},
> +@samp{until}, @samp{finish}, or expression evaluation only the current
> +thread may run.
> +
> +@item step
> +When @code{on} the scheduler is locked for stepping commands during
> +normal execution and record modes.  This mode optimizes for single-stepping;
> +only the current thread is resumed while you are stepping, so that the focus
> +of debugging does not change unexpectedly.
> +
> +@end table

The above description lost the information about the default setting.
To understand what is the default, the reader needs to read further,
where you describe the old settings ("shortcuts") and their
equivalence to the new one; this is sub-optimal.

> -Only the current thread may run when the inferior is resumed.  New
> +Only the current thread may run when the inferior is resumed.   New
                                                                ^^^
Redundant whitespace there.

Thanks.

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

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 4358494a6b6..bdbbadacb89 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -18,6 +18,23 @@  disassemble
 
 * New commands
 
+set scheduler-locking replay run | replay step | run | step (on|off)
+show scheduler-locking (replay run | replay step | run | step)
+  Extend the scheduler locking settings with a set of set/show
+  commands, which can be used individually to control the scheduler during
+  various commands.
+    'replay run' -- when on, the scheduler is locked during non-stepping
+    commands in replay mode.
+    'replay step' -- when on, the scheduler is locked during stepping
+    commands in replay mode.
+    'run' -- when on, the scheduler is locked during non-stepping commands
+    in normal mode.
+    'step' -- when on, the scheduler is locked during stepping commands
+    in normal mode.
+  The older scheduler locking settings can be used as shortcuts, their behavior
+  is not changed.
+  The output of "show scheduler-locking" has changed to support the new settings.
+
 info missing-debug-handler
   List all the registered missing debug handlers.
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 42a91235661..0560b9a5ea7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7110,22 +7110,56 @@  On some OSes, you can modify @value{GDBN}'s default behavior by
 locking the OS scheduler to allow only a single thread to run.
 
 @table @code
-@item set scheduler-locking @var{mode}
-@cindex scheduler-locking
+
+@item set scheduler-locking @var{type} [@code{on}|@code{off}]
+@cindex scheduler locking type
+@cindex lock scheduler
+Set the scheduler locking settings.  It applies to normal execution,
+record mode, and replay mode.  The scheduler locking can be set separately
+for stepping and non-stepping commands.
+
+@table @code
+@item replay run
+When @code{on} the scheduler is locked for non-stepping commands during
+replay mode.  For commands like @samp{continue}, @samp{until}, @samp{finish},
+or expression evaluation only the current thread may run.
+
+@item replay step
+When @code{on} the scheduler is locked for stepping commands during replay
+mode.  This mode optimizes for single-stepping; only the current thread is
+resumed while you are stepping, so that the focus of debugging does not change
+unexpectedly.
+
+@item run
+When @code{on} the scheduler is locked for non-stepping commands during
+normal execution and record modes.  For commands like @samp{continue},
+@samp{until}, @samp{finish}, or expression evaluation only the current
+thread may run.
+
+@item step
+When @code{on} the scheduler is locked for stepping commands during
+normal execution and record modes.  This mode optimizes for single-stepping;
+only the current thread is resumed while you are stepping, so that the focus
+of debugging does not change unexpectedly.
+
+@end table
+
+@item set scheduler-locking @var{shortcut-mode}
 @cindex scheduler locking mode
 @cindex lock scheduler
-Set the scheduler locking mode.  It applies to normal execution,
-record mode, and replay mode.  @var{mode} can be one of
-the following:
+One can choose to not set the scheduler locking settings individually but use
+the following predefined shortcut modes.
 
 @table @code
 @item off
-There is no locking and any thread may run at any time.
+There is no locking and any thread may run at any time.  This is equivalent to
+setting all options to @code{off}.
 
 @item on
-Only the current thread may run when the inferior is resumed.  New
+Only the current thread may run when the inferior is resumed.   New
 threads created by the resumed thread are held stopped at their entry
-point, before they execute any instruction.
+point, before they execute any instruction.  This is
+equivalent to setting all options to @code{on}.
 
 @item step
 Behaves like @code{on} when stepping, and @code{off} otherwise.
@@ -7140,9 +7174,18 @@  another thread hits a breakpoint during its timeslice, @value{GDBN}
 does not change the current thread away from the thread that you are
 debugging.
 
+This is equivalent to set @samp{scheduler-locking step} and
+@samp{scheduler-locking replay step} to @code{on}, while other settings
+are @code{off}.
+
 @item replay
 Behaves like @code{on} in replay mode, and @code{off} in either record
 mode or during normal execution.  This is the default mode.
+
+This is equivalent to set @samp{scheduler-locking replay run} and
+@samp{scheduler-locking replay step} to @code{on}, while other settings
+are @code{off}.
+
 @end table
 
 @item show scheduler-locking
@@ -33949,7 +33992,7 @@  the end or beginning of a replay log if one is being used.
 @end itemize
 In all-stop mode (@pxref{All-Stop
 Mode}), may resume only one thread, or all threads, depending on the
-value of the @samp{scheduler-locking} variable.  If @samp{--all} is
+value of the @samp{scheduler-locking} variables.  If @samp{--all} is
 specified, all threads (in all inferiors) will be resumed.  The @samp{--all} option is
 ignored in all-stop mode.  If the @samp{--thread-group} options is
 specified, then all threads in that thread group are resumed.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index f70f615abf8..daf8cecd601 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -77,6 +77,7 @@ 
 #include "extension.h"
 #include "disasm.h"
 #include "interps.h"
+#include "cli/cli-decode.h"
 
 /* Prototypes for local functions */
 
@@ -116,6 +117,12 @@  struct schedlock_options;
 static bool schedlock_applies_to_opts (const schedlock_options &opts,
 				       bool step);
 
+/* Command lists for the scheduler locking.  */
+static cmd_list_element *schedlock_set_cmdlist;
+static cmd_list_element *schedlock_show_cmdlist;
+static cmd_list_element *schedlock_set_replay_cmdlist;
+static cmd_list_element *schedlock_show_replay_cmdlist;
+
 /* Asynchronous signal handler registered as event loop source for
    when we have pending events ready to be passed to the core.  */
 static struct async_event_handler *infrun_async_inferior_event_token;
@@ -2348,7 +2355,8 @@  struct schedlock_options
 
     operator bool () const { return value; }
     const char *c_str () const { return value ? "on" : "off"; }
-    /* Set new value.  Return true, if the value has changed.  */
+    /* Set new value.  Return true, if the value has changed.
+       Also notifies the observer, if the value has changed.  */
     bool set (bool new_value);
   };
 
@@ -2375,6 +2383,8 @@  schedlock_options::option::set (bool new_value)
   if (value != new_value)
     {
       value = new_value;
+      std::string param_name = "scheduler-locking " + name;
+      interps_notify_param_changed (param_name.c_str (), c_str ());
       return true;
     }
 
@@ -2395,15 +2405,6 @@  static const char schedlock_off[] = "off";
 static const char schedlock_on[] = "on";
 static const char schedlock_step[] = "step";
 static const char schedlock_replay[] = "replay";
-static const char *const scheduler_enums[] = {
-  schedlock_off,
-  schedlock_on,
-  schedlock_step,
-  schedlock_replay,
-  nullptr
-};
-
-static const char *scheduler_mode = schedlock_replay;
 
 schedlock schedlock {{{"run", false}, {"step", false}},
 		     {{"replay run", true}, {"replay step", true}}};
@@ -2424,35 +2425,89 @@  set_schedlock_shortcut_option (const char *shortcut)
   /* Check that we got a valid shortcut option.  */
   gdb_assert (is_on || is_step || is_replay || is_off);
 
-  schedlock.normal.run.set (is_on);
-  schedlock.normal.step.set (is_on || is_step);
-  schedlock.replay.run.set (is_on || is_replay);
-  schedlock.replay.step.set (is_on || is_replay || is_step);
+  bool any_changed = schedlock.normal.run.set (is_on);
+  any_changed = schedlock.normal.step.set (is_on || is_step) || any_changed;
+  any_changed = schedlock.replay.run.set (is_on || is_replay) || any_changed;
+  any_changed = schedlock.replay.step.set (is_on || is_replay || is_step)
+    || any_changed;
+
+  /* If at least one parameter has changed, notify the observer
+     in the old-fashioned way.  */
+  if (any_changed)
+    interps_notify_param_changed ("scheduler-locking", shortcut);
 }
 
+/* Default callback for set methods of scheduler locking options.
+   Checks that the scheduler locking is supported.
+   If no, it reverts all options to "off" and throws an error.  */
+
 static void
-show_scheduler_mode (struct ui_file *file, int from_tty,
-		     struct cmd_list_element *c, const char *value)
+set_schedlock_callback (const char *args, int from_tty, cmd_list_element *c)
 {
-  gdb_printf (file,
-	      _("Mode for locking scheduler "
-		"during execution is \"%s\".\n"),
-	      value);
+  if (target_can_lock_scheduler ())
+    return;
+
+  /* Set scheduler locking off and error out.  */
+  set_schedlock_shortcut_option (schedlock_off);
+  error (_("Target '%s' cannot support this command."), target_shortname ());
 }
 
+/* Support for shortcut schedlock options: "on", "off", "step", "replay".  */
+
 static void
-set_schedlock_func (const char *args, int from_tty, struct cmd_list_element *c)
+set_schedlock_step (const char *args, int from_tty, cmd_list_element *c)
 {
-  if (!target_can_lock_scheduler ())
-    {
-      scheduler_mode = schedlock_off;
-      /* Set scheduler locking off.  */
-      set_schedlock_shortcut_option (schedlock_off);
-      error (_("Target '%s' cannot support this command."),
-	     target_shortname ());
-    }
+  if (!args || !*args)
+    set_schedlock_shortcut_option (schedlock_step);
+  set_schedlock_callback (args, from_tty, nullptr);
+}
+
+static void
+set_schedlock_replay (const char *args, int from_tty)
+{
+  set_schedlock_shortcut_option (schedlock_replay);
+  set_schedlock_callback (args, from_tty, nullptr);
+}
 
-  set_schedlock_shortcut_option (scheduler_mode);
+static void
+set_schedlock_on (const char *args, int from_tty)
+{
+  set_schedlock_shortcut_option (schedlock_on);
+  set_schedlock_callback (args, from_tty, nullptr);
+}
+
+static void
+set_schedlock_off (const char *args, int from_tty)
+{
+  set_schedlock_shortcut_option (schedlock_off);
+  set_schedlock_callback (args, from_tty, nullptr);
+}
+
+/* Default method to show a single option of scheduler locking.  */
+
+static void
+show_schedlock_option (ui_file *file, int from_tty,
+		       cmd_list_element *c, const char *value)
+{
+  gdb_assert (c->prefix != nullptr);
+  const char *mode;
+  if (strcmp (c->prefix->name, "replay") == 0)
+    mode = "replay mode";
+  else if (strcmp (c->prefix->name, "scheduler-locking") == 0)
+    mode = "normal execution";
+  else
+    gdb_assert_not_reached ("Unexpected command prefix.");
+
+  const char *type;
+  if (strcmp (c->name, "step") == 0)
+    type = "stepping commands";
+  else if (strcmp (c->name, "run") == 0)
+    type = "non-stepping commands";
+  else
+    gdb_assert_not_reached ("Unexpected command name.");
+
+  gdb_printf (file, _("\"%s\"\tScheduler locking for %s is "
+		      "\"%s\" during the %s.\n"), value, type, value, mode);
 }
 
 /* True if execution commands resume all threads of all processes by
@@ -8354,14 +8409,6 @@  switch_back_to_stepped_thread (struct execution_control_state *ecs)
 	  return true;
 	}
 
-      /* If scheduler locking applies even if not stepping, there's no
-	 need to walk over threads.  Above we've checked whether the
-	 current thread is stepping.  If some other thread not the
-	 event thread is stepping, then it must be that scheduler
-	 locking is not in effect.  */
-      if (schedlock_applies_to_thread (ecs->event_thread))
-	return false;
-
       /* Otherwise, we no longer expect a trap in the current thread.
 	 Clear the trap_expected flag before switching back -- this is
 	 what keep_going does as well, if we call it.  */
@@ -10652,21 +10699,92 @@  By default, the debugger will use the same inferior."),
 			show_follow_exec_mode_string,
 			&setlist, &showlist);
 
-  add_setshow_enum_cmd ("scheduler-locking", class_run, 
-			scheduler_enums, &scheduler_mode, _("\
-Set mode for locking scheduler during execution."), _("\
-Show mode for locking scheduler during execution."), _("\
-off    == no locking (threads may preempt at any time)\n\
-on     == full locking (no thread except the current thread may run)\n\
-	  This applies to both normal execution and replay mode.\n\
-step   == scheduler locked during stepping commands (step, next, stepi, nexti).\n\
-	  In this mode, other threads may run during other commands.\n\
-	  This applies to both normal execution and replay mode.\n\
-replay == scheduler locked in replay mode and unlocked during normal execution."),
-			set_schedlock_func,	/* traps on target vector */
-			show_scheduler_mode,
+  /* Commands for set/show scheduler-locking.  */
+
+  add_setshow_prefix_cmd ("scheduler-locking", class_run, _("\
+Scheduler locking settings.\n\
+Configure scheduler locking settings in various conditions."), _("\
+Show scheduler locking settings in various conditions."),
+			&schedlock_set_cmdlist,
+			&schedlock_show_cmdlist,
 			&setlist, &showlist);
 
+  add_setshow_boolean_cmd ("run", class_run, &schedlock.normal.run.value, _("\
+Scheduler locking for non-stepping commands during normal execution."), _("\
+Show scheduler locking for non-stepping commands during normal execution."),
+			   _("\
+Controls scheduler locking for non-stepping commands during normal execution.\n\
+Commands include continue, until, finish.  The setting does not affect \
+stepping."),
+			   set_schedlock_callback,
+			   show_schedlock_option,
+			   &schedlock_set_cmdlist,
+			   &schedlock_show_cmdlist);
+
+  add_setshow_boolean_cmd ("step", class_run, &schedlock.normal.step.value, _("\
+Scheduler locking for stepping commands.  W/o arguments locks the scheduler \
+for stepping."), _("\
+Show scheduler locking for stepping commands during normal execution."), _("\
+If argument \"on\" or \"off\", sets scheduler locking behavior for stepping\n\
+commands only during normal execution.\n\
+Commands include step, next, stepi, nexti."),
+			   set_schedlock_step,
+			   show_schedlock_option,
+			   &schedlock_set_cmdlist,
+			   &schedlock_show_cmdlist);
+
+  /* Commands for set/show scheduler-locking in replay mode.
+     The base command adds support for the shortcut
+       set scheduler-locking replay
+     command.  */
+
+  add_setshow_prefix_cmd ("replay", class_run, _("\
+Scheduler locking settings for replay mode.\n\
+Configure scheduler locking in various conditions such as during continuing\n\
+or stepping."),
+("Show scheduler locking in replay mode."),
+			&schedlock_set_replay_cmdlist,
+			&schedlock_show_replay_cmdlist,
+			&schedlock_set_cmdlist,
+			&schedlock_show_cmdlist);
+  add_prefix_cmd ("replay", class_run, set_schedlock_replay, _("\
+Scheduler locking settings for replay mode. \
+W/o arguments completely locks the scheduler in replay mode."),
+		  &schedlock_set_replay_cmdlist,
+	   0, &schedlock_set_cmdlist);
+
+  add_setshow_boolean_cmd ("run", class_run, &schedlock.replay.run.value, _("\
+Set scheduler locking for non-stepping commands in replay mode."), _("\
+Show scheduler locking for non-stepping commands in replay mode."), _("\
+Controls scheduler locking for non-stepping commands in replay mode.\n\
+Commands include continue, until, finish.  The setting does not affect \
+stepping."),
+			   set_schedlock_callback,
+			   show_schedlock_option,
+			   &schedlock_set_replay_cmdlist,
+			   &schedlock_show_replay_cmdlist);
+
+  add_setshow_boolean_cmd ("step", class_run, &schedlock.replay.step.value, _("\
+Set scheduler locking for stepping commands in replay mode."), _("\
+Show scheduler locking for stepping commands in replay mode."), _("\
+Controls scheduler locking for stepping commands in replay mode.\n\
+Commands include step, next, stepi, nexti."),
+			   set_schedlock_callback,
+			   show_schedlock_option,
+			   &schedlock_set_replay_cmdlist,
+			   &schedlock_show_replay_cmdlist);
+
+/* Commands "set scheduler-locking on" and "set scheduler-locking off"
+   are provided for backward compatibility.  */
+  c = add_cmd ("on", class_run, set_schedlock_on, _("\
+[Shortcut] Full locking (no thread except the current thread may run).\n\
+This applies to both normal execution and replay mode."),
+	   &schedlock_set_cmdlist);
+
+  c = add_cmd ("off", class_run, set_schedlock_off, _("\
+[Shortcut] No locking (threads may preempt at any time)."),
+	   &schedlock_set_cmdlist);
+
   add_setshow_boolean_cmd ("schedule-multiple", class_run, &sched_multi, _("\
 Set mode for resuming threads of all processes."), _("\
 Show mode for resuming threads of all processes."), _("\
diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
index 4889c31aff3..625a47fb745 100644
--- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
+++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
@@ -255,17 +255,12 @@  proc make_cli_in_mi_re { command cli_in_mi_mode mode event inf cli_thread
 # Return the current value of the "scheduler-locking" parameter.
 
 proc show_scheduler_locking { } {
-    global gdb_prompt
-    global expect_out
-
-    set any "\[^\r\n\]*"
-
     set test "show scheduler-locking"
-    gdb_test_multiple $test $test {
-	-re ".*Mode for locking scheduler during execution is \"(${any})\".\r\n$gdb_prompt " {
-	    pass $test
-	    return $expect_out(1,string)
-	}
+    set schedlock [get_scheduler_locking $test]
+
+    if {$schedlock ne "unknown"} {
+	pass $test
+	return $schedlock
     }
 
     error "Couldn't get current scheduler-locking value."
@@ -342,7 +337,10 @@  proc test_continue_to_start { mode inf } {
 		}
 
 		# Restore scheduler-locking to its original value.
-		gdb_test_no_output "set scheduler-locking $previous_schedlock_val"
+		gdb_test_no_output "set scheduler-locking replay run [lindex $previous_schedlock_val 0]"
+		gdb_test_no_output "set scheduler-locking replay step [lindex $previous_schedlock_val 1]"
+		gdb_test_no_output "set scheduler-locking run [lindex $previous_schedlock_val 2]"
+		gdb_test_no_output "set scheduler-locking step [lindex $previous_schedlock_val 3]"
 	    } else { # $mode == "non-stop"
 		# Put a thread-specific breakpoint for thread 2 of the current
 		# inferior.  We don't put a breakpoint for thread 3, since we
diff --git a/gdb/testsuite/gdb.threads/hand-call-in-threads.exp b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp
index 58039ddd30e..d18254910db 100644
--- a/gdb/testsuite/gdb.threads/hand-call-in-threads.exp
+++ b/gdb/testsuite/gdb.threads/hand-call-in-threads.exp
@@ -68,7 +68,9 @@  gdb_test "continue" \
 # Before we start making hand function calls, turn on scheduler locking.
 
 gdb_test_no_output "set scheduler-locking on" "enable scheduler locking"
-gdb_test "show scheduler-locking" ".* locking scheduler .* is \"on\"." "show scheduler locking on"
+set test "show scheduler-locking on"
+gdb_assert {[get_scheduler_locking $test {"on" "on" "on" "on"}] ne "unknown"} \
+    $test
 
 # Now hand-call a function in each thread, having the function
 # stop without returning.
@@ -139,7 +141,9 @@  gdb_test_multiple "maint print dummy-frames" "all dummies popped" {
 
 # Before we resume the full program, turn off scheduler locking.
 gdb_test_no_output "set scheduler-locking off" "disable scheduler locking"
-gdb_test "show scheduler-locking" ".* locking scheduler .* is \"off\"." "show scheduler locking off"
+set test "show scheduler-locking off"
+gdb_assert {[get_scheduler_locking $test {"off" "off" "off" "off"}] ne "unknown"} \
+    $test
 
 # Continue one last time, the program should exit normally.
 #
diff --git a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp
index bd037a02674..69e750fea44 100644
--- a/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp
+++ b/gdb/testsuite/gdb.threads/multiple-successive-infcall.exp
@@ -49,8 +49,9 @@  foreach_with_prefix thread {5 4 3}  {
 gdb_breakpoint [gdb_get_line_number "testmarker01"]
 gdb_continue_to_breakpoint "testmarker01"
 gdb_test_no_output "set scheduler-locking on"
-gdb_test "show scheduler-locking" \
-  "Mode for locking scheduler during execution is \"on\"."
+set test "show scheduler-locking"
+gdb_assert {[get_scheduler_locking $test {"on" "on" "on" "on"}] ne "unknown"} \
+		$test
 
 foreach_with_prefix thread {5 4 3 2 1}  {
   gdb_test "thread ${thread}" "Switching to .*"
diff --git a/gdb/testsuite/gdb.threads/schedlock.exp b/gdb/testsuite/gdb.threads/schedlock.exp
index 3c60f6b3478..cf11fd3a0a0 100644
--- a/gdb/testsuite/gdb.threads/schedlock.exp
+++ b/gdb/testsuite/gdb.threads/schedlock.exp
@@ -94,7 +94,8 @@  proc get_current_thread { description } {
 # Make sure we're stopped in the loop, in one of the non-main threads.
 
 proc goto_loop { msg } {
-    gdb_breakpoint [concat [gdb_get_line_number "schedlock.exp: main loop"] " if arg != 0"]
+    global srcfile
+    gdb_breakpoint [concat "$srcfile:" [gdb_get_line_number "schedlock.exp: main loop"] " if arg != 0"]
 
     set test "return to loop"
     if {$msg != ""} {
@@ -264,16 +265,21 @@  with_test_prefix "schedlock=on: cmd=continue" {
 }
 
 # Test stepping/nexting with different modes of scheduler locking.
-proc test_step { schedlock cmd call_function } {
+# Do scheduler-locking off setting before the test if PRESET_SCHEDLOCK_OFF is 1.
+# LOCKED defines whether we expect the thread to be locked.  If -1, then
+# determine it first.
+proc test_step { schedlock cmd call_function { preset_schedlock_off 1 } { locked -1 } } {
     global NUM
 
-    gdb_test_no_output "set scheduler-locking off"
+    if {$preset_schedlock_off} {
+	gdb_test_no_output "set scheduler-locking off"
+    }
     goto_loop ""
 
     set curthread [get_current_thread "before"]
 
     # No need to set to off again.  This avoids a duplicate message.
-    if {$schedlock != "off"} {
+    if {$preset_schedlock_off && $schedlock != "off"} {
 	gdb_test_no_output "set scheduler-locking $schedlock"
     }
 
@@ -284,16 +290,17 @@  proc test_step { schedlock cmd call_function } {
 
     step_ten_loops $cmd
 
-    if { $schedlock == "on" || $schedlock == "step" } {
-	set locked 1
-    } else {
-	set locked 0
+    if { $locked == -1 } {
+	if { $schedlock == "on" || $schedlock == "step"} {
+	    set locked 1
+	} else {
+	    set locked 0
+	}
     }
-
     check_result $cmd $curthread $before_args $locked
 }
 
-# Test stepping/nexting with different modes of scheduler locking.
+# Test stepping/nexting with different shortcut modes of scheduler locking.
 foreach schedlock {"off" "step" "on"} {
     with_test_prefix "schedlock=$schedlock" {
 	with_test_prefix "cmd=step" {
@@ -312,3 +319,55 @@  foreach schedlock {"off" "step" "on"} {
 	}
     }
 }
+
+proc test_schedlock_opts {run step} {
+    set test "show scheduler-locking"
+    if {[get_scheduler_locking $test [list "off" "off" $run $step]] eq "unknown"} {
+	fail $test
+    } else {
+	pass $test
+    }
+
+    set locked 0
+    if {$step eq "on"} {
+	set locked 1
+    }
+
+    # Stepping tests.
+    with_test_prefix "cmd=step" {
+	test_step "" "step" 0 0 $locked
+    }
+    with_test_prefix "cmd=next" {
+	foreach call_function {0 1} {
+	    with_test_prefix "call_function=$call_function" {
+		test_step "" "next" $call_function 0 $locked
+	    }
+	}
+    }
+
+    # Continuing tests.
+    set locked 0
+    if {$run eq "on"} {
+	set locked 1
+    }
+    with_test_prefix "cmd=continue" {
+	# Use whichever we stopped in.
+	set curthread [get_current_thread "before"]
+	set cont_args [get_args "before"]
+	my_continue "continue"
+	check_result "continue" $curthread $cont_args $locked
+    }
+}
+
+gdb_test_no_output "set scheduler-locking off"
+
+# Test different options of scheduler locking.
+foreach run {"off" "on"} {
+    foreach step {"off" "on"} {
+	with_test_prefix "run=$run step=$step" {
+	    gdb_test_no_output "set scheduler-locking run $run"
+	    gdb_test_no_output "set scheduler-locking step $step"
+	    test_schedlock_opts $run $step
+	}
+    }
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index eb8f6998b1e..a7c85c2b20a 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -8565,6 +8565,42 @@  gdb_caching_proc gdb_target_symbol_prefix {} {
     return $prefix
 }
 
+proc get_scheduler_locking {{test ""} {expected ""}} {
+    global gdb_prompt
+    if {$test eq ""} {
+	set test "reading current scheduler-locking mode"
+    }
+
+    set any "\[^\r\n\]+"
+    set on_off "\(?:on|off\)"
+
+    set i [expr 4 - [llength $expected]]
+    while {$i > 0} {
+	incr i -1
+	lappend expected $on_off
+    }
+    set schedlock_regex \
+	[multi_line \
+	     "${any}replay run: +\"\([lindex $expected 0]\)\"${any}non-stepping${any}replay${any}" \
+	     "${any}replay step: +\"\([lindex $expected 1]\)\"${any}stepping${any}replay${any}" \
+	     "${any}run: +\"\([lindex $expected 2]\)\"${any}non-stepping${any}normal execution${any}" \
+	     "${any}step: +\"\([lindex $expected 3]\)\"${any}stepping${any}normal execution${any}"]
+
+    set current_schedule_locking_mode "unknown"
+    gdb_test_multiple "show scheduler-locking" $test {
+	-re -wrap $schedlock_regex {
+	    set current_schedule_locking_mode \
+		[list $expect_out(1,string) \
+		     $expect_out(2,string) \
+		     $expect_out(3,string) \
+		     $expect_out(4,string)]
+	}
+	-re -wrap "" {}
+	timeout {}
+    }
+    return $current_schedule_locking_mode
+}
+
 # Return 1 if target supports scheduler locking, otherwise return 0.
 
 gdb_caching_proc target_supports_scheduler_locking {} {
@@ -8586,21 +8622,16 @@  gdb_caching_proc target_supports_scheduler_locking {} {
     set current_schedule_locking_mode ""
 
     set test "reading current scheduler-locking mode"
-    gdb_test_multiple "show scheduler-locking" $test {
-	-re "Mode for locking scheduler during execution is \"(\[\^\"\]*)\".*$gdb_prompt" {
-	    set current_schedule_locking_mode $expect_out(1,string)
-	}
-	-re "$gdb_prompt $" {
-	    set supports_schedule_locking 0
-	}
-	timeout {
-	    set supports_schedule_locking 0
-	}
+    set current_schedule_locking_mode [get_scheduler_locking $test]
+    if { $current_scheduler_locking eq "unknown" } {
+	set supports_schedule_locking 0
     }
 
     if { $supports_schedule_locking == -1 } {
 	set test "checking for scheduler-locking support"
-	gdb_test_multiple "set scheduler-locking $current_schedule_locking_mode" $test {
+
+	# Try to set scheduler-locking run.
+	gdb_test_multiple "set scheduler-locking  run [lindex $current_schedule_locking_mode 0]" $test {
 	    -re "Target '\[^'\]+' cannot support this command\..*$gdb_prompt $" {
 		set supports_schedule_locking 0
 	    }