diff mbox

[v2] Add a 'starti' command.

Message ID 20170901214521.82007-1-jhb@FreeBSD.org
State New
Headers show

Commit Message

John Baldwin Sept. 1, 2017, 9:45 p.m. UTC
This works like 'start' but it stops at the first instruction rather than
the first line in main().  This is useful if one wants to single step
through runtime linker startup.

While here, introduce a RUN_ARGS_HELP macro for shared help text between
run, start, and starti.  This includes expanding the help for start and
starti to include details from run's help text.

gdb/ChangeLog:

	* NEWS (Changes since GDB 8.0): Add starti.
	* infcmd.c (enum run_break): New.
	(run_command_1): Insert temporary breakpoint at current PC for
	RUN_BREAK_AT_FIRST_INSN case.
	(run_command): Use enum run_break.
	(start_command): Likewise.
	(starti_command): New function.
	(RUN_ARGS_HELP): New macro.
	(_initialize_infcmd): Use RUN_ARGS_HELP for run and start
	commands.  Add starti command.

gdb/doc/ChangeLog:

	* gdb.texinfo (Starting your Program): Add description of
	starti command.  Mention starti command as an alternative for
	debugging the elaboration phase.

gdb/testsuite/ChangeLog:

	* gdb.base/starti.c: New file.
	* gdb.base/starti.exp: New file.
	* lib/gdb.exp (gdb_starti_cmd): New procedure.
---
 gdb/ChangeLog                     | 13 ++++++
 gdb/NEWS                          |  3 ++
 gdb/doc/ChangeLog                 |  6 +++
 gdb/doc/gdb.texinfo               | 18 ++++++--
 gdb/infcmd.c                      | 95 +++++++++++++++++++++++++++++----------
 gdb/testsuite/ChangeLog           |  6 +++
 gdb/testsuite/gdb.base/starti.c   | 30 +++++++++++++
 gdb/testsuite/gdb.base/starti.exp | 37 +++++++++++++++
 gdb/testsuite/lib/gdb.exp         | 37 +++++++++++++++
 9 files changed, 218 insertions(+), 27 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/starti.c
 create mode 100644 gdb/testsuite/gdb.base/starti.exp
diff mbox

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 1d50e2c5b7..fe1e529448 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,16 @@ 
+2017-08-31  John Baldwin  <jhb@FreeBSD.org>
+
+	* NEWS (Changes since GDB 8.0): Add starti.
+	* infcmd.c (enum run_break): New.
+	(run_command_1): Insert temporary breakpoint at current PC for
+	RUN_BREAK_AT_FIRST_INSN case.
+	(run_command): Use enum run_break.
+	(start_command): Likewise.
+	(starti_command): New function.
+	(RUN_ARGS_HELP): New macro.
+	(_initialize_infcmd): Use RUN_ARGS_HELP for run and start
+	commands.  Add starti command.
+
 2017-08-31  Weimin Pan  <weimin.pan@oracle.com>
 
 	* sparc64-tdep.c (adi_stat_t): Fix comment formatting.
diff --git a/gdb/NEWS b/gdb/NEWS
index 735415495e..c0b2a909ca 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -35,6 +35,9 @@  set debug separate-debug-file
 show debug separate-debug-file
   Control the display of debug output about separate debug file search.
 
+starti
+  Start the debugged program stopping at the first instruction.
+
 * TUI Single-Key mode now supports two new shortcut keys: `i' for stepi and
   `o' for nexti.
 
diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog
index bf82730830..b0d58fd5b3 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,3 +1,9 @@ 
+2017-08-31  John Baldwin  <jhb@FreeBSD.org>
+
+	* gdb.texinfo (Starting your Program): Add description of
+	starti command.  Mention starti command as an alternative for
+	debugging the elaboration phase.
+
 2017-08-23  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	* gdb.texinfo (Compiling and Injecting Code): Add to subsection
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d977b234d0..2bf2cb6f1b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2117,10 +2117,20 @@  reused if no argument is provided during subsequent calls to
 @samp{start} or @samp{run}.
 
 It is sometimes necessary to debug the program during elaboration.  In
-these cases, using the @code{start} command would stop the execution of
-your program too late, as the program would have already completed the
-elaboration phase.  Under these circumstances, insert breakpoints in your
-elaboration code before running your program.
+these cases, using the @code{start} command would stop the execution
+of your program too late, as the program would have already completed
+the elaboration phase.  Under these circumstances, either insert
+breakpoints in your elaboration code before running your program or
+use the @code{starti} command.
+
+@kindex starti
+@item starti
+@cindex run to first instruction
+The @samp{starti} command does the equivalent of setting a temporary
+breakpoint at the first instruction of a program's execution and then
+invoking the @samp{run} command.  For programs containing an
+elaboration phase, the @code{starti} command will stop execution at
+the start of the elaboration phase.
 
 @anchor{set exec-wrapper}
 @kindex set exec-wrapper
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index bd9ead8a45..4fc5c3f1da 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -520,12 +520,25 @@  prepare_execution_command (struct target_ops *target, int background)
     }
 }
 
-/* Implement the "run" command.  If TBREAK_AT_MAIN is set, then insert
-   a temporary breakpoint at the begining of the main program before
-   running the program.  */
+/* Determine which temporary breakpoints run_command_1 will
+   create.  */
+
+enum run_break {
+  /* Do not create any breakpoint.  */
+  RUN_BREAK_NONE,
+
+  /* Stop at the beginning of the program's main function.  */
+  RUN_BREAK_AT_MAIN,
+
+  /* Stop at the first instruction of the program.  */
+  RUN_BREAK_AT_FIRST_INSN
+};
+
+/* Implement the "run" command.  Insert a temporary breakpoint if
+   requested by RUN_BREAK before running the program.  */
 
 static void
-run_command_1 (char *args, int from_tty, int tbreak_at_main)
+run_command_1 (char *args, int from_tty, enum run_break run_break)
 {
   const char *exec_file;
   struct cleanup *old_chain;
@@ -534,6 +547,7 @@  run_command_1 (char *args, int from_tty, int tbreak_at_main)
   struct target_ops *run_target;
   int async_exec;
   struct cleanup *args_chain;
+  CORE_ADDR pc;
 
   dont_repeat ();
 
@@ -571,8 +585,8 @@  run_command_1 (char *args, int from_tty, int tbreak_at_main)
 
   /* Done.  Can now set breakpoints, change inferior args, etc.  */
 
-  /* Insert the temporary breakpoint if a location was specified.  */
-  if (tbreak_at_main)
+  /* Insert temporary breakpoint in main function if requested.  */
+  if (run_break == RUN_BREAK_AT_MAIN)
     tbreak_command (main_name (), 0);
 
   exec_file = get_exec_file (0);
@@ -632,9 +646,25 @@  run_command_1 (char *args, int from_tty, int tbreak_at_main)
      has done its thing; now we are setting up the running program.  */
   post_create_inferior (&current_target, 0);
 
+  /* Insert temporary breakpoint at first instruction if requested.  */
+  pc = regcache_read_pc (get_current_regcache ());
+  if (run_break == RUN_BREAK_AT_FIRST_INSN)
+    {
+      event_location_up location = new_address_location (pc, NULL, 0);
+
+      create_breakpoint (get_current_arch (), location.get (),
+			 NULL /* cond_string  */, -1 /* thread  */,
+			 NULL /* extra_string  */, 0 /* parse_extra  */,
+			 1 /* tempflag  */, bp_breakpoint,
+			 0 /* ignore_count  */,
+			 AUTO_BOOLEAN_FALSE /* pending_break_support  */,
+			 &bkpt_breakpoint_ops /* ops  */, 0 /* from_tty  */,
+			 1 /* enabled  */, 0 /* internal  */, 0 /* flags  */);
+    }
+
   /* Start the target running.  Do not use -1 continuation as it would skip
      breakpoint right at the entry point.  */
-  proceed (regcache_read_pc (get_current_regcache ()), GDB_SIGNAL_0);
+  proceed (pc, GDB_SIGNAL_0);
 
   /* Since there was no error, there's no need to finish the thread
      states here.  */
@@ -644,7 +674,7 @@  run_command_1 (char *args, int from_tty, int tbreak_at_main)
 static void
 run_command (char *args, int from_tty)
 {
-  run_command_1 (args, from_tty, 0);
+  run_command_1 (args, from_tty, RUN_BREAK_NONE);
 }
 
 /* Start the execution of the program up until the beginning of the main
@@ -660,7 +690,15 @@  start_command (char *args, int from_tty)
     error (_("No symbol table loaded.  Use the \"file\" command."));
 
   /* Run the program until reaching the main procedure...  */
-  run_command_1 (args, from_tty, 1);
+  run_command_1 (args, from_tty, RUN_BREAK_AT_MAIN);
+}
+
+/* Start the execution of the program until the first instruction.  */
+
+static void
+starti_command (char *args, int from_tty)
+{
+  run_command_1 (args, from_tty, RUN_BREAK_AT_FIRST_INSN);
 } 
 
 static int
@@ -3189,6 +3227,22 @@  info_proc_cmd_all (char *args, int from_tty)
   info_proc_cmd_1 (args, IP_ALL, from_tty);
 }
 
+/* This help string is used for the run, start, and starti commands.
+   It is defined as a macro to prevent duplication.  */
+
+#define RUN_ARGS_HELP \
+"You may specify arguments to give it.\n\
+Args may include \"*\", or \"[...]\"; they are expanded using the\n\
+shell that will start the program (specified by the \"$SHELL\"\
+environment\nvariable).  Input and output redirection with \">\",\
+\"<\", or \">>\"\nare also allowed.\n\n\
+With no arguments, uses arguments last specified (with \"run\" \
+or \"set args\").\n\
+To cancel previous arguments and run with no arguments,\n\
+use \"set args\" without arguments.\n\
+To start the inferior without using a shell, use \"set \
+startup-with-shell off\"."
+
 void
 _initialize_infcmd (void)
 {
@@ -3395,24 +3449,19 @@  Specifying -a and an ignore count simultaneously is an error."));
   add_com_alias ("fg", "cont", class_run, 1);
 
   c = add_com ("run", class_run, run_command, _("\
-Start debugged program.  You may specify arguments to give it.\n\
-Args may include \"*\", or \"[...]\"; they are expanded using the\n\
-shell that will start the program (specified by the \"$SHELL\"\
-environment\nvariable).  Input and output redirection with \">\",\
-\"<\", or \">>\"\nare also allowed.\n\n\
-With no arguments, uses arguments last specified (with \"run\" \
-or \"set args\").\n\
-To cancel previous arguments and run with no arguments,\n\
-use \"set args\" without arguments.\n\
-To start the inferior without using a shell, use \"set \
-startup-with-shell off\"."));
+Start debugged program.\n"
+RUN_ARGS_HELP));
   set_cmd_completer (c, filename_completer);
   add_com_alias ("r", "run", class_run, 1);
 
   c = add_com ("start", class_run, start_command, _("\
-Run the debugged program until the beginning of the main procedure.\n\
-You may specify arguments to give to your program, just as with the\n\
-\"run\" command."));
+Start the debugged program stopping at the beginning of the main procedure.\n"
+RUN_ARGS_HELP));
+  set_cmd_completer (c, filename_completer);
+
+  c = add_com ("starti", class_run, starti_command, _("\
+Start the debugged program stopping at the first instruction.\n"
+RUN_ARGS_HELP));
   set_cmd_completer (c, filename_completer);
 
   add_com ("interrupt", class_run, interrupt_command,
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index aa1d6b8660..0d40bc9fdf 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,9 @@ 
+2017-08-31  John Baldwin  <jhb@FreeBSD.org>
+
+	* gdb.base/starti.c: New file.
+	* gdb.base/starti.exp: New file.
+	* lib/gdb.exp (gdb_starti_cmd): New procedure.
+
 2017-08-28  Simon Marchi  <simon.marchi@ericsson.com>
 
 	* gdb.base/commands.exp (gdbvar_simple_if_test,
diff --git a/gdb/testsuite/gdb.base/starti.c b/gdb/testsuite/gdb.base/starti.c
new file mode 100644
index 0000000000..dc098fe8aa
--- /dev/null
+++ b/gdb/testsuite/gdb.base/starti.c
@@ -0,0 +1,30 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#include <stdio.h>
+
+int x;
+
+__attribute__((constructor)) void ctor()
+{
+  x = 1;
+}
+
+int main()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/starti.exp b/gdb/testsuite/gdb.base/starti.exp
new file mode 100644
index 0000000000..90405e0113
--- /dev/null
+++ b/gdb/testsuite/gdb.base/starti.exp
@@ -0,0 +1,37 @@ 
+# Copyright 2017 Free Software Foundation, Inc.
+
+# 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/>.
+
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile debug]} {
+    return -1
+}
+
+# The program should stop somewhere.
+if { [gdb_starti_cmd] < 0 } {
+    untested starti
+    return -1
+}
+
+gdb_test "" \
+         "Temporary breakpoint.*" \
+         "starti"
+
+gdb_test "p x" "\\$. = 0"
+gdb_breakpoint main
+gdb_test "continue" "Breakpoint .*main \\(\\) at .*starti.c.*"
+gdb_test "p x" "\\$. = 1"
+gdb_test "continue" "exited.*"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index c0ecab31ff..1338caae2b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -370,6 +370,43 @@  proc gdb_start_cmd {args} {
     return -1
 }
 
+# Generic starti command.  Return 0 if we could start the program, -1
+# if we could not.
+#
+# N.B. This function does not wait for gdb to return to the prompt,
+# that is the caller's responsibility.
+
+proc gdb_starti_cmd {args} {
+    global gdb_prompt use_gdb_stub
+
+    foreach command [gdb_init_commands] {
+	send_gdb "$command\n"
+	gdb_expect 30 {
+	    -re "$gdb_prompt $" { }
+	    default {
+		perror "gdb_init_command for target failed"
+		return -1
+	    }
+	}
+    }
+
+    if $use_gdb_stub {
+	return -1
+    }
+
+    send_gdb "starti $args\n"
+    gdb_expect 60 {
+	-re "The program .* has been started already.*y or n. $" {
+	    send_gdb "y\n"
+	    exp_continue
+	}
+	-re "Starting program: \[^\r\n\]*" {
+	    return 0
+	}
+    }
+    return -1
+}
+
 # Set a breakpoint at FUNCTION.  If there is an additional argument it is
 # a list of options; the supported options are allow-pending, temporary,
 # message, no-message, and passfail.