* [rfc] Multiple process support in gdbserver
@ 2007-12-08 10:54 Daniel Jacobowitz
2007-12-08 15:29 ` Eli Zaretskii
0 siblings, 1 reply; 11+ messages in thread
From: Daniel Jacobowitz @ 2007-12-08 10:54 UTC (permalink / raw)
To: gdb-patches
This patch allows gdbserver (in "extended-remote" mode) to create new
processes, attach to running processes, and persist when no process
is running. It works pretty much the same as a native GDB once
you connect. Documentation, NEWS, and tests are all included.
Tested using gdbserver on x86_64.
All comments welcome!
--
Daniel Jacobowitz
CodeSourcery
2007-12-07 Daniel Jacobowitz <dan@codesourcery.com>
* linux-low.c (linux_attach_lwp): Do not _exit after errors.
(linux_kill, linux_detach): Clean up the process list.
* remote-utils.c (remote_open): Improve port number parsing.
(putpkt_binary, input_interrupt): Only send interrupts if the target
is running.
* server.c (extended_protocol): Make static.
(attached): Define earlier.
(exit_requested, response_needed, program_argv): New variables.
(target_running): New.
(start_inferior): Clear attached here.
(attach_inferior): Set attached here.
(require_running): Define.
(handle_query): Use require_running and target_running. Implement
"monitor exit".
(handle_v_attach, handle_v_run): New.
(handle_v_requests): Use require_running. Handle vAttach and vRun.
(gdbserver_usage): Update.
(main): Redo argument parsing. Handle --debug and --multi. Handle
--attach along with other options or after the port. Save
program_argv. Support no initial program. Resynchronize
communication with GDB after an error. Handle "monitor exit".
Use require_running and target_running. Always allow the extended
protocol. Do not error out for Hc0 or Hc-1. Do not automatically
restart in extended mode.
* README: Refer to the GDB manual. Update --attach usage.
2007-12-07 Daniel Jacobowitz <dan@codesourcery.com>
* remote.c (struct remote_state): Add cached_wait_status.
(remote_exec_file): New variable.
(PACKET_vAttach, PACKET_vRun): New constants.
(extended_remote_restart): Do not query for status.
(struct start_remote_args): New.
(remote_start_remote): Take it as a second argument. Check
whether the target is running. Issue an error for non-running
non-extended targets. Cache the wait status. Set inferior_ptid
here.
(remote_open_1): Prompt to disconnect non-running targets. Make
sure the target is marked running. Do not set inferior_ptid here.
Update call to remote_start_remote. Do not call remote_check_symbols
if the target is not running.
(remote_detach_1): Rename from remote_detach. Take an EXTENDED
argument. Handle a non-running target.
(remote_detach): Use it.
(extended_remote_detach): New.
(remote_disconnect): Fix typo. Use remoute_mourn_1.
(extended_remote_attach_1, extended_remote_attach)
(extended_async_remote_attach): New.
(remote_vcont_resume): Remove unused variable.
(remote_wait, remote_async_wait): Use any cached wait status.
(putpkt_binary, getpkt): Clear any cached wait status.
(extended_remoute_mourn_1): New.
(extended_remote_mourn): Use it.
(extended_async_remote_mourn, extended_remote_run): New.
(extended_remote_create_inferior_1): New.
(extended_remote_create_inferior): Use it.
(extended_remote_async_create_inferior): Likewise.
(remote_xfer_partial): Skip for non-executing targets.
(init_extended_remote_ops): Set to_detach and to_attach.
(init_extended_async_remote_ops): Likewise. Use
extended_async_remote_mourn.
(_initialize_remote): Register vAttach, vRun, and
set remote exec-file.
* NEWS: Mention vAttach, vRun, and gdbserver extended-remote support.
2007-12-07 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.server/ext-attach.c, gdb.server/ext-attach.exp,
gdb.server/ext-run.exp: New files.
* lib/gdbserver-support.exp (gdbserver_download): New.
(gdbserver_start): New. Update gdbserver expected
output.
(gdbserver_spawn): Use them.
(gdbserver_start_extended): New.
2007-12-07 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.texinfo (Using the `gdbserver' Program): Add security
warning. Rearrange into subsections and subsubsections. Document
--multi and --debug. Correct --with-sysroot typo. Update --attach
usage. Make load reference clearer. Document monitor exit.
(Remote Configuration): Document set remote exec-file, attach-packet,
and run-packet.
(Packets): Document vAttach and vRun. Correct syntax error.
---
gdb/NEWS | 10
gdb/doc/gdb.texinfo | 130 +++++++-
gdb/gdbserver/README | 4
gdb/gdbserver/linux-low.c | 24 +
gdb/gdbserver/remote-utils.c | 12
gdb/gdbserver/server.c | 472 +++++++++++++++++++++++-------
gdb/remote.c | 497 +++++++++++++++++++++++++-------
gdb/testsuite/gdb.server/ext-attach.c | 31 +
gdb/testsuite/gdb.server/ext-attach.exp | 77 ++++
gdb/testsuite/gdb.server/ext-run.exp | 48 +++
gdb/testsuite/lib/gdbserver-support.exp | 69 +++-
11 files changed, 1114 insertions(+), 260 deletions(-)
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/doc/gdb.texinfo 2007-12-07 15:22:05.000000000 -0500
@@ -12877,9 +12877,19 @@ choice for debugging.
or a TCP connection, using the standard @value{GDBN} remote serial
protocol.
-@table @emph
-@item On the target machine,
-you need to have a copy of the program you want to debug.
+@quotation
+@emph{Warning:} @code{gdbserver} does not have any built-in security.
+Do not run @code{gdbserver} connected to any public network; a
+@value{GDBN} connection to @code{gdbserver} provides access to the
+target system with the same privileges as the user running
+@code{gdbserver}.
+@end quotation
+
+@subsection Running @code{gdbserver}
+@cindex arguments, to @code{gdbserver}
+
+Run @code{gdbserver} on the target system. You need a copy of the
+program you want to debug, including any libraries it requires.
@code{gdbserver} does not need your program's symbol table, so you can
strip the program if necessary to save space. @value{GDBN} on the host
system does all the symbol handling.
@@ -12922,11 +12932,13 @@ conflicts with another service, @code{gd
and exits.} You must use the same port number with the host @value{GDBN}
@code{target remote} command.
+@subsubsection Attaching to a Running Program
+
On some targets, @code{gdbserver} can also attach to running programs.
This is accomplished via the @code{--attach} argument. The syntax is:
@smallexample
-target> gdbserver @var{comm} --attach @var{pid}
+target> gdbserver --attach @var{comm} @var{pid}
@end smallexample
@var{pid} is the process ID of a currently running process. It isn't necessary
@@ -12938,18 +12950,54 @@ You can debug processes by name instead
@code{pidof} utility:
@smallexample
-target> gdbserver @var{comm} --attach `pidof @var{program}`
+target> gdbserver --attach @var{comm} `pidof @var{program}`
@end smallexample
In case more than one copy of @var{program} is running, or @var{program}
has multiple threads, most versions of @code{pidof} support the
@code{-s} option to only return the first process ID.
-@item On the host machine,
-first make sure you have the necessary symbol files. Load symbols for
+@subsubsection Multi-Process Mode for @code{gdbserver}
+
+When you connect to @code{gdbserver} using @code{target remote},
+@code{gdbserver} debugs the specified program only once. When the
+program exits, or you detach from it, @value{GDBN} closes the connection
+and @code{gdbserver} exits.
+
+If you connect using @code{target extended-remote}, @code{gdbserver}
+enters multi-process mode. When the debugged program exits, or you
+detach from it, @value{GDBN} stays connected to @code{gdbserver} even
+though no program is running. The @code{run} and @code{attach}
+commands instruct @code{gdbserver} to run or attach to a new program.
+The @code{run} command uses @code{set remote exec-file} (@pxref{set
+remote exec-file}) to select the program to run. Command line
+arguments are supported, except for wildcard expansion and I/O
+redirection (@pxref{Arguments}).
+
+To start @code{gdbserver} without supplying an initial command to run
+or process ID to attach, use the @option{--multi} command line option.
+Then you can connect using @code{target extended-remote} and start
+the program you want to debug.
+
+@code{gdbserver} does not automatically exit in multi-process mode.
+You can terminate it by using @code{monitor exit}
+(@pxref{Monitor Commands for gdbserver}).
+
+@subsubsection Other Command-Line Arguments for @code{gdbserver}
+
+You can include @option{--debug} on the @code{gdbserver} command line.
+@code{gdbserver} will display extra status information about the debugging
+process. This option is intended for @code{gdbserver} development and
+for bug reports to the developers.
+
+@subsection Connecting to @code{gdbserver}
+
+Run @value{GDBN} on the host system.
+
+First make sure you have the necessary symbol files. Load symbols for
your application using the @code{file} command before you connect. Use
@code{set sysroot} to locate target libraries (unless your @value{GDBN}
-was compiled with the correct sysroot using @code{--with-system-root}).
+was compiled with the correct sysroot using @code{--with-sysroot}).
The symbol file and target libraries must exactly match the executable
and libraries on the target, with one exception: the files on the host
@@ -12963,19 +13011,17 @@ Connect to your target (@pxref{Connectin
For TCP connections, you must start up @code{gdbserver} prior to using
the @code{target remote} command. Otherwise you may get an error whose
text depends on the host system, but which usually looks something like
-@samp{Connection refused}. You don't need to use the @code{load}
+@samp{Connection refused}. Don't use the @code{load}
command in @value{GDBN} when using @code{gdbserver}, since the program is
already on the target.
-@end table
-
@subsection Monitor Commands for @code{gdbserver}
@cindex monitor commands, for @code{gdbserver}
+@anchor{Monitor Commands for gdbserver}
During a @value{GDBN} session using @code{gdbserver}, you can use the
@code{monitor} command to send special requests to @code{gdbserver}.
-Here are the available commands; they are only of interest when
-debugging @value{GDBN} or @code{gdbserver}.
+Here are the available commands.
@table @code
@item monitor help
@@ -12990,6 +13036,11 @@ Disable or enable general debugging mess
Disable or enable specific debugging messages associated with the remote
protocol (@pxref{Remote Protocol}).
+@item monitor exit
+Tell gdbserver to exit immediately. This command should be followed by
+@code{disconnect} to close the debugging session. @code{gdbserver} will
+detach from any attached processes and kill any processes it created.
+
@end table
@node Remote Configuration
@@ -13084,6 +13135,15 @@ responses.
@itemx set remote hardware-breakpoint-limit @var{limit}
Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or
watchpoints. A limit of -1, the default, is treated as unlimited.
+
+@item set remote exec-file @var{filename}
+@itemx show remote exec-file
+@anchor{set remote exec-file}
+@cindex executable file, for remote target
+Select the file used for @code{run} with @code{target
+extended-remote}. This should be set to a filename valid on the
+target system. If it is not set, the target will use a default
+filename (e.g.@: the last program run).
@end table
@cindex remote packets, enabling and disabling
@@ -13130,10 +13190,18 @@ are:
@tab @code{qSymbol}
@tab Detecting multiple threads
+@item @code{attach}
+@tab @code{vAttach}
+@tab @code{attach}
+
@item @code{verbose-resume}
@tab @code{vCont}
@tab Stepping or resuming multiple threads
+@item @code{run}
+@tab @code{vRun}
+@tab @code{run}
+
@item @code{software-breakpoint}
@tab @code{Z0}
@tab @code{break}
@@ -23520,6 +23588,22 @@ thread is dead
Packets starting with @samp{v} are identified by a multi-letter name,
up to the first @samp{;} or @samp{?} (or the end of the packet).
+@item vAttach;@var{pid}
+@cindex @samp{vAttach} packet
+Attach to a new process with the specified process ID. @var{pid} is a
+hexadecimal integer identifying the process. If the stub is currently
+controlling a process, it is killed. The attached process is stopped.
+
+This packet is only available in extended mode.
+
+Reply:
+@table @samp
+@item E @var{nn}
+for an error
+@item @r{Any stop packet}
+for success (@pxref{Stop Reply Packets})
+@end table
+
@item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{}
@cindex @samp{vCont} packet
Resume the inferior, specifying different actions for each thread.
@@ -23616,6 +23700,24 @@ The stub is permitted to delay or batch
regions of flash memory are unpredictable until the @samp{vFlashDone}
request is completed.
+@item vRun;@var{filename}@r{[};@var{argument}@r{]}@dots{}
+@cindex @samp{vRun} packet
+Run the program @var{filename}, passing it each @var{argument} on its
+command line. The file and arguments are hex-encoded strings. If
+@var{filename} is an empty string, the stub may use a default program
+(e.g.@: the last program run). The program is created in the stopped
+state. If the stub is currently controlling a process, it is killed.
+
+This packet is only available in extended mode.
+
+Reply:
+@table @samp
+@item E @var{nn}
+for an error
+@item @r{Any stop packet}
+for success (@pxref{Stop Reply Packets})
+@end table
+
@item X @var{addr},@var{length}:@var{XX@dots{}}
@anchor{X packet}
@cindex @samp{X} packet
@@ -24775,7 +24877,7 @@ return -1 if an error occurs. @var{path
@var{flags} is an integer indicating a mask of open flags
(@pxref{Open Flags}), and @var{mode} is an integer indicating a mask
of mode bits to use if the file is created (@pxref{mode_t Values}).
-@xref{open} for details of the open flags and mode values.
+@ref{open} for details of the open flags and mode values.
@item vFile:close: @var{fd}
Close the open file corresponding to @var{fd} and return 0, or
Index: src/gdb/gdbserver/README
===================================================================
--- src.orig/gdb/gdbserver/README 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/README 2007-12-07 10:38:30.000000000 -0500
@@ -9,6 +9,8 @@ host. GDB and GDBserver communicate usi
implemented in remote.c, and various *-stub.c files. They communicate via
either a serial line or a TCP connection.
+For more information about GDBserver, see the GDB manual.
+
Usage (server (target) side):
First, you need to have a copy of the program you want to debug put onto
@@ -47,7 +49,7 @@ print an error message and exit.
On some targets, gdbserver can also attach to running programs. This is
accomplished via the --attach argument. The syntax is:
- target> gdbserver COMM --attach PID
+ target> gdbserver --attach COMM PID
PID is the process ID of a currently running process. It isn't necessary
to point gdbserver at a binary for the running process.
Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/linux-low.c 2007-12-07 10:38:30.000000000 -0500
@@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid)
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
{
- fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
+ if (all_threads.head != NULL)
+ {
+ /* If we fail to attach to an LWP, just warn. */
+ fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
+ strerror (errno), errno);
+ fflush (stderr);
+ return;
+ }
+ else
+ /* If we fail to attach to a process, report an error. */
+ error ("Cannot attach to process %ld: %s (%d)\n", pid,
strerror (errno), errno);
- fflush (stderr);
-
- /* If we fail to attach to an LWP, just return. */
- if (all_threads.head == NULL)
- _exit (0177);
- return;
}
ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE);
@@ -396,6 +400,10 @@ linux_kill (void)
/* Make sure it died. The loop is most likely unnecessary. */
wstat = linux_wait_for_event (thread);
} while (WIFSTOPPED (wstat));
+
+ clear_inferiors ();
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
}
static void
@@ -434,6 +442,8 @@ linux_detach (void)
delete_all_breakpoints ();
for_each_inferior (&all_threads, linux_detach_one_process);
clear_inferiors ();
+ free (all_processes.head);
+ all_processes.head = all_processes.tail = NULL;
return 0;
}
Index: src/gdb/gdbserver/remote-utils.c
===================================================================
--- src.orig/gdb/gdbserver/remote-utils.c 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/gdbserver/remote-utils.c 2007-12-07 10:38:30.000000000 -0500
@@ -186,15 +186,15 @@ remote_open (char *name)
#ifdef USE_WIN32API
static int winsock_initialized;
#endif
- char *port_str;
int port;
struct sockaddr_in sockaddr;
socklen_t tmp;
int tmp_desc;
+ char *port_end;
- port_str = strchr (name, ':');
-
- port = atoi (port_str + 1);
+ port = strtoul (port_str + 1, &port_end, 10);
+ if (port_str[1] == '\0' || *port_end != '\0')
+ fatal ("Bad port argument: %s", name);
#ifdef USE_WIN32API
if (!winsock_initialized)
@@ -574,7 +574,7 @@ putpkt_binary (char *buf, int cnt)
}
/* Check for an input interrupt while we're here. */
- if (buf3[0] == '\003')
+ if (buf3[0] == '\003' && current_inferior != NULL)
(*the_target->request_interrupt) ();
}
while (buf3[0] != '+');
@@ -616,7 +616,7 @@ input_interrupt (int unused)
cc = read (remote_desc, &c, 1);
- if (cc != 1 || c != '\003')
+ if (cc != 1 || c != '\003' || current_inferior == NULL)
{
fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n",
cc, c, c);
Index: src/gdb/gdbserver/server.c
===================================================================
--- src.orig/gdb/gdbserver/server.c 2007-12-07 10:38:17.000000000 -0500
+++ src/gdb/gdbserver/server.c 2007-12-07 14:26:13.000000000 -0500
@@ -34,9 +34,15 @@ unsigned long general_thread;
unsigned long step_thread;
unsigned long thread_from_wait;
unsigned long old_thread_from_wait;
-int extended_protocol;
int server_waiting;
+static int extended_protocol;
+static int attached;
+static int response_needed;
+static int exit_requested;
+
+static char **program_argv;
+
/* Enable miscellaneous debugging output. The name is historical - it
was originally used to debug LinuxThreads support. */
int debug_threads;
@@ -69,8 +75,16 @@ restore_old_foreground_pgrp (void)
#endif
static int
+target_running (void)
+{
+ return all_threads.head != NULL;
+}
+
+static int
start_inferior (char *argv[], char *statusptr)
{
+ attached = 0;
+
#ifdef SIGTTOU
signal (SIGTTOU, SIG_DFL);
signal (SIGTTIN, SIG_DFL);
@@ -107,6 +121,8 @@ attach_inferior (int pid, char *statuspt
if (myattach (pid) != 0)
return -1;
+ attached = 1;
+
fprintf (stderr, "Attached; pid = %d\n", pid);
fflush (stderr);
@@ -254,6 +270,13 @@ monitor_show_help (void)
monitor_output (" Enable remote protocol debugging messages\n");
}
+#define require_running(BUF) \
+ if (!target_running ()) \
+ { \
+ write_enn (BUF); \
+ return; \
+ }
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -263,6 +286,7 @@ handle_query (char *own_buf, int packet_
/* Reply the current thread id. */
if (strcmp ("qC", own_buf) == 0)
{
+ require_running (own_buf);
thread_ptr = all_threads.head;
sprintf (own_buf, "QC%x",
thread_to_gdb_id ((struct thread_info *)thread_ptr));
@@ -271,7 +295,7 @@ handle_query (char *own_buf, int packet_
if (strcmp ("qSymbol::", own_buf) == 0)
{
- if (the_target->look_up_symbols != NULL)
+ if (target_running () && the_target->look_up_symbols != NULL)
(*the_target->look_up_symbols) ();
strcpy (own_buf, "OK");
@@ -280,6 +304,7 @@ handle_query (char *own_buf, int packet_
if (strcmp ("qfThreadInfo", own_buf) == 0)
{
+ require_running (own_buf);
thread_ptr = all_threads.head;
sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
thread_ptr = thread_ptr->next;
@@ -288,6 +313,7 @@ handle_query (char *own_buf, int packet_
if (strcmp ("qsThreadInfo", own_buf) == 0)
{
+ require_running (own_buf);
if (thread_ptr != NULL)
{
sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr));
@@ -305,7 +331,8 @@ handle_query (char *own_buf, int packet_
&& strcmp ("qOffsets", own_buf) == 0)
{
CORE_ADDR text, data;
-
+
+ require_running (own_buf);
if (the_target->read_offsets (&text, &data))
sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX",
(long)text, (long)data, (long)data);
@@ -324,6 +351,7 @@ handle_query (char *own_buf, int packet_
CORE_ADDR ofs;
unsigned char *spu_buf;
+ require_running (own_buf);
strcpy (own_buf, "E00");
if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0)
return;
@@ -356,6 +384,7 @@ handle_query (char *own_buf, int packet_
CORE_ADDR ofs;
unsigned char *spu_buf;
+ require_running (own_buf);
strcpy (own_buf, "E00");
spu_buf = malloc (packet_len - 15);
if (!spu_buf)
@@ -387,6 +416,8 @@ handle_query (char *own_buf, int packet_
unsigned int len;
char *annex;
+ require_running (own_buf);
+
/* Reject any annex; grab the offset and length. */
if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0
|| annex[0] != '\0')
@@ -420,6 +451,8 @@ handle_query (char *own_buf, int packet_
const char *document;
char *annex;
+ require_running (own_buf);
+
/* Check for support. */
document = get_features_xml ("target.xml");
if (document == NULL)
@@ -467,6 +500,8 @@ handle_query (char *own_buf, int packet_
struct inferior_list_entry *dll_ptr;
char *annex;
+ require_running (own_buf);
+
/* Reject any annex; grab the offset and length. */
if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0
|| annex[0] != '\0')
@@ -535,7 +570,7 @@ handle_query (char *own_buf, int packet_
if (the_target->read_auxv != NULL)
strcat (own_buf, ";qXfer:auxv:read+");
-
+
if (the_target->qxfer_spu != NULL)
strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+");
@@ -553,6 +588,8 @@ handle_query (char *own_buf, int packet_
CORE_ADDR parts[3], address = 0;
int i, err;
+ require_running (own_buf);
+
for (i = 0; i < 3; i++)
{
char *p2;
@@ -642,6 +679,8 @@ handle_query (char *own_buf, int packet_
}
else if (strcmp (mon, "help") == 0)
monitor_show_help ();
+ else if (strcmp (mon, "exit") == 0)
+ exit_requested = 1;
else
{
monitor_output ("Unknown monitor command.\n\n");
@@ -772,6 +811,95 @@ err:
return;
}
+/* Attach to a new program. Return 1 if successful, 0 if failure. */
+int
+handle_v_attach (char *own_buf, char *status, int *signal)
+{
+ int pid;
+
+ pid = strtol (own_buf + 8, NULL, 16);
+ if (pid != 0 && attach_inferior (pid, status, signal) == 0)
+ {
+ prepare_resume_reply (own_buf, *status, *signal);
+ return 1;
+ }
+ else
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+}
+
+/* Run a new program. Return 1 if successful, 0 if failure. */
+static int
+handle_v_run (char *own_buf, char *status, int *signal)
+{
+ char *p, **pp, *next_p, **new_argv;
+ int i, new_argc;
+
+ new_argc = 0;
+ for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';'))
+ {
+ p++;
+ new_argc++;
+ }
+
+ new_argv = malloc ((new_argc + 2) * sizeof (char *));
+ i = 0;
+ for (p = own_buf + strlen ("vRun;"); *p; p = next_p)
+ {
+ next_p = strchr (p, ';');
+ if (next_p == NULL)
+ next_p = p + strlen (p);
+
+ if (i == 0 && p == next_p)
+ new_argv[i] = NULL;
+ else
+ {
+ new_argv[i] = malloc (1 + (next_p - p) / 2);
+ unhexify (new_argv[i], p, (next_p - p) / 2);
+ new_argv[i][(next_p - p) / 2] = '\0';
+ }
+
+ if (*next_p)
+ next_p++;
+ i++;
+ }
+ new_argv[i] = NULL;
+
+ if (new_argv[0] == NULL)
+ {
+ if (program_argv == NULL)
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+
+ new_argv[0] = strdup (program_argv[0]);
+ }
+
+ /* Free the old argv. */
+ if (program_argv)
+ {
+ for (pp = program_argv; *pp != NULL; pp++)
+ free (*pp);
+ free (program_argv);
+ }
+ program_argv = new_argv;
+
+ *signal = start_inferior (program_argv, status);
+ if (*status == 'T')
+ {
+ prepare_resume_reply (own_buf, *status, *signal);
+ return 1;
+ }
+ else
+ {
+ write_enn (own_buf);
+ return 0;
+ }
+}
+
/* Handle all of the extended 'v' packets. */
void
handle_v_requests (char *own_buf, char *status, int *signal,
@@ -779,6 +907,7 @@ handle_v_requests (char *own_buf, char *
{
if (strncmp (own_buf, "vCont;", 6) == 0)
{
+ require_running (own_buf);
handle_v_cont (own_buf, status, signal);
return;
}
@@ -793,6 +922,28 @@ handle_v_requests (char *own_buf, char *
&& handle_vFile (own_buf, packet_len, new_packet_len))
return;
+ if (strncmp (own_buf, "vAttach;", 8) == 0)
+ {
+ if (target_running ())
+ {
+ fprintf (stderr, "Killing inferior\n");
+ kill_inferior ();
+ }
+ handle_v_attach (own_buf, status, signal);
+ return;
+ }
+
+ if (strncmp (own_buf, "vRun;", 5) == 0)
+ {
+ if (target_running ())
+ {
+ fprintf (stderr, "Killing inferior\n");
+ kill_inferior ();
+ }
+ handle_v_run (own_buf, status, signal);
+ return;
+ }
+
/* Otherwise we didn't know what packet it was. Say we didn't
understand it. */
own_buf[0] = 0;
@@ -829,8 +980,6 @@ myresume (char *own_buf, int step, int *
disable_async_io ();
}
-static int attached;
-
static void
gdbserver_version (void)
{
@@ -844,13 +993,25 @@ gdbserver_version (void)
static void
gdbserver_usage (void)
{
- printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n"
- "\tgdbserver COMM --attach PID\n"
+ printf ("Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n"
+ "\tgdbserver [OPTIONS] --attach COMM PID\n"
+ "\tgdbserver [OPTIONS] --multi COMM\n"
"\n"
"COMM may either be a tty device (for serial debugging), or \n"
- "HOST:PORT to listen for a TCP connection.\n");
+ "HOST:PORT to listen for a TCP connection.\n"
+ "\n"
+ "Options:\n"
+ " --debug\t\tEnable debugging output.\n");
}
+#undef require_running
+#define require_running(BUF) \
+ if (!target_running ()) \
+ { \
+ write_enn (BUF); \
+ break; \
+ }
+
int
main (int argc, char *argv[])
{
@@ -862,18 +1023,38 @@ main (int argc, char *argv[])
CORE_ADDR mem_addr;
int bad_attach;
int pid;
- char *arg_end;
+ char *arg_end, *port;
+ char **next_arg = &argv[1];
+ int multi_mode = 0;
+ int attach = 0;
+ int was_running;
- if (argc >= 2 && strcmp (argv[1], "--version") == 0)
+ while (*next_arg != NULL && **next_arg == '-')
{
- gdbserver_version ();
- exit (0);
- }
+ if (strcmp (*next_arg, "--version") == 0)
+ {
+ gdbserver_version ();
+ exit (0);
+ }
+ else if (strcmp (*next_arg, "--help") == 0)
+ {
+ gdbserver_usage ();
+ exit (0);
+ }
+ else if (strcmp (*next_arg, "--attach") == 0)
+ attach = 1;
+ else if (strcmp (*next_arg, "--multi") == 0)
+ multi_mode = 1;
+ else if (strcmp (*next_arg, "--debug") == 0)
+ debug_threads = 1;
+ else
+ {
+ fprintf (stderr, "Unknown argument: %s\n", *next_arg);
+ exit (1);
+ }
- if (argc >= 2 && strcmp (argv[1], "--help") == 0)
- {
- gdbserver_usage ();
- exit (0);
+ next_arg++;
+ continue;
}
if (setjmp (toplevel))
@@ -882,23 +1063,34 @@ main (int argc, char *argv[])
exit (1);
}
- bad_attach = 0;
- pid = 0;
- attached = 0;
- if (argc >= 3 && strcmp (argv[2], "--attach") == 0)
+ port = *next_arg;
+ next_arg++;
+ if (port == NULL || (!attach && !multi_mode && *next_arg == NULL))
{
- if (argc == 4
- && argv[3][0] != '\0'
- && (pid = strtoul (argv[3], &arg_end, 0)) != 0
- && *arg_end == '\0')
- {
- ;
- }
- else
- bad_attach = 1;
+ gdbserver_usage ();
+ exit (1);
}
- if (argc < 3 || bad_attach)
+ bad_attach = 0;
+ pid = 0;
+
+ /* --attach used to come after PORT, so allow it there for
+ compatibility. */
+ if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0)
+ {
+ attach = 1;
+ next_arg++;
+ }
+
+ if (attach
+ && (*next_arg == NULL
+ || (*next_arg)[0] == '\0'
+ || (pid = strtoul (*next_arg, &arg_end, 0)) == 0
+ || *arg_end != '\0'
+ || next_arg[1] != NULL))
+ bad_attach = 1;
+
+ if (bad_attach)
{
gdbserver_usage ();
exit (1);
@@ -910,26 +1102,34 @@ main (int argc, char *argv[])
own_buf = malloc (PBUFSIZ + 1);
mem_buf = malloc (PBUFSIZ);
- if (pid == 0)
+ if (pid == 0 && *next_arg != NULL)
{
+ int i, n;
+
+ n = argc - (next_arg - argv);
+ program_argv = malloc (sizeof (char *) * (n + 1));
+ for (i = 0; i < n; i++)
+ program_argv[i] = strdup (next_arg[i]);
+ program_argv[i] = NULL;
+
/* Wait till we are at first instruction in program. */
- signal = start_inferior (&argv[2], &status);
+ signal = start_inferior (program_argv, &status);
/* We are now (hopefully) stopped at the first instruction of
the target process. This assumes that the target process was
successfully created. */
}
+ else if (pid != 0)
+ {
+ if (attach_inferior (pid, &status, &signal) == -1)
+ error ("Attaching not supported on this target");
+
+ /* Otherwise succeeded. */
+ }
else
{
- switch (attach_inferior (pid, &status, &signal))
- {
- case -1:
- error ("Attaching not supported on this target");
- break;
- default:
- attached = 1;
- break;
- }
+ status = 'W';
+ signal = 0;
}
/* Don't report shared library events on the initial connection,
@@ -945,27 +1145,43 @@ main (int argc, char *argv[])
}
if (status == 'W' || status == 'X')
+ was_running = 0;
+ else
+ was_running = 1;
+
+ if (!was_running && !multi_mode)
{
- fprintf (stderr, "No inferior, GDBserver exiting.\n");
+ fprintf (stderr, "No program to debug. GDBserver exiting.\n");
exit (1);
}
while (1)
{
- remote_open (argv[1]);
+ remote_open (port);
restart:
- setjmp (toplevel);
+ if (setjmp (toplevel) != 0)
+ {
+ /* An error occurred. */
+ if (response_needed)
+ {
+ write_enn (own_buf);
+ putpkt (own_buf);
+ }
+ }
+
disable_async_io ();
- while (1)
+ while (!exit_requested)
{
unsigned char sig;
int packet_len;
int new_packet_len = -1;
+ response_needed = 0;
packet_len = getpkt (own_buf);
if (packet_len <= 0)
break;
+ response_needed = 1;
i = 0;
ch = own_buf[i++];
@@ -978,39 +1194,38 @@ main (int argc, char *argv[])
handle_general_set (own_buf);
break;
case 'D':
+ require_running (own_buf);
fprintf (stderr, "Detaching from inferior\n");
if (detach_inferior () != 0)
- {
- write_enn (own_buf);
- putpkt (own_buf);
- }
+ write_enn (own_buf);
else
{
write_ok (own_buf);
- putpkt (own_buf);
- remote_close ();
- /* If we are attached, then we can exit. Otherwise, we
- need to hang around doing nothing, until the child
- is gone. */
- if (!attached)
- join_inferior ();
+ if (extended_protocol)
+ {
+ /* Treat this like a normal program exit. */
+ signal = 0;
+ status = 'W';
+ }
+ else
+ {
+ putpkt (own_buf);
+ remote_close ();
- exit (0);
+ /* If we are attached, then we can exit. Otherwise, we
+ need to hang around doing nothing, until the child
+ is gone. */
+ if (!attached)
+ join_inferior ();
+
+ exit (0);
+ }
}
+ break;
case '!':
- if (attached == 0)
- {
- extended_protocol = 1;
- prepare_resume_reply (own_buf, status, signal);
- }
- else
- {
- /* We can not use the extended protocol if we are
- attached, because we can not restart the running
- program. So return unrecognized. */
- own_buf[0] = '\0';
- }
+ extended_protocol = 1;
+ write_ok (own_buf);
break;
case '?':
prepare_resume_reply (own_buf, status, signal);
@@ -1020,12 +1235,18 @@ main (int argc, char *argv[])
{
unsigned long gdb_id, thread_id;
+ require_running (own_buf);
gdb_id = strtoul (&own_buf[2], NULL, 16);
- thread_id = gdb_id_to_thread_id (gdb_id);
- if (thread_id == 0)
+ if (gdb_id == 0 || gdb_id == -1)
+ thread_id = gdb_id;
+ else
{
- write_enn (own_buf);
- break;
+ thread_id = gdb_id_to_thread_id (gdb_id);
+ if (thread_id == 0)
+ {
+ write_enn (own_buf);
+ break;
+ }
}
if (own_buf[1] == 'g')
@@ -1048,15 +1269,18 @@ main (int argc, char *argv[])
}
break;
case 'g':
+ require_running (own_buf);
set_desired_inferior (1);
registers_to_string (own_buf);
break;
case 'G':
+ require_running (own_buf);
set_desired_inferior (1);
registers_from_string (&own_buf[1]);
write_ok (own_buf);
break;
case 'm':
+ require_running (own_buf);
decode_m_packet (&own_buf[1], &mem_addr, &len);
if (read_inferior_memory (mem_addr, mem_buf, len) == 0)
convert_int_to_ascii (mem_buf, own_buf, len);
@@ -1064,6 +1288,7 @@ main (int argc, char *argv[])
write_enn (own_buf);
break;
case 'M':
+ require_running (own_buf);
decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf);
if (write_inferior_memory (mem_addr, mem_buf, len) == 0)
write_ok (own_buf);
@@ -1071,6 +1296,7 @@ main (int argc, char *argv[])
write_enn (own_buf);
break;
case 'X':
+ require_running (own_buf);
if (decode_X_packet (&own_buf[1], packet_len - 1,
&mem_addr, &len, mem_buf) < 0
|| write_inferior_memory (mem_addr, mem_buf, len) != 0)
@@ -1079,6 +1305,7 @@ main (int argc, char *argv[])
write_ok (own_buf);
break;
case 'C':
+ require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig);
@@ -1087,6 +1314,7 @@ main (int argc, char *argv[])
myresume (own_buf, 0, &signal, &status);
break;
case 'S':
+ require_running (own_buf);
convert_ascii_to_int (own_buf + 1, &sig, 1);
if (target_signal_to_host_p (sig))
signal = target_signal_to_host (sig);
@@ -1095,10 +1323,12 @@ main (int argc, char *argv[])
myresume (own_buf, 1, &signal, &status);
break;
case 'c':
+ require_running (own_buf);
signal = 0;
myresume (own_buf, 0, &signal, &status);
break;
case 's':
+ require_running (own_buf);
signal = 0;
myresume (own_buf, 1, &signal, &status);
break;
@@ -1121,6 +1351,7 @@ main (int argc, char *argv[])
{
int res;
+ require_running (own_buf);
res = (*the_target->insert_watchpoint) (type, addr, len);
if (res == 0)
write_ok (own_buf);
@@ -1151,6 +1382,7 @@ main (int argc, char *argv[])
{
int res;
+ require_running (own_buf);
res = (*the_target->remove_watchpoint) (type, addr, len);
if (res == 0)
write_ok (own_buf);
@@ -1163,20 +1395,24 @@ main (int argc, char *argv[])
break;
}
case 'k':
+ response_needed = 0;
+ if (!target_running ())
+ /* The packet we received doesn't make sense - but we
+ can't reply to it, either. */
+ goto restart;
+
fprintf (stderr, "Killing inferior\n");
kill_inferior ();
- /* When using the extended protocol, we start up a new
- debugging session. The traditional protocol will
- exit instead. */
+
+ /* When using the extended protocol, we wait with no
+ program running. The traditional protocol will exit
+ instead. */
if (extended_protocol)
{
- write_ok (own_buf);
- fprintf (stderr, "GDBserver restarting\n");
-
- /* Wait till we are at 1st instruction in prog. */
- signal = start_inferior (&argv[2], &status);
+ status = 'X';
+ signal = TARGET_SIGNAL_KILL;
+ was_running = 0;
goto restart;
- break;
}
else
{
@@ -1187,6 +1423,7 @@ main (int argc, char *argv[])
{
unsigned long gdb_id, thread_id;
+ require_running (own_buf);
gdb_id = strtoul (&own_buf[1], NULL, 16);
thread_id = gdb_id_to_thread_id (gdb_id);
if (thread_id == 0)
@@ -1202,18 +1439,25 @@ main (int argc, char *argv[])
}
break;
case 'R':
+ response_needed = 0;
+
/* Restarting the inferior is only supported in the
extended protocol. */
if (extended_protocol)
{
- kill_inferior ();
- write_ok (own_buf);
+ if (target_running ())
+ kill_inferior ();
fprintf (stderr, "GDBserver restarting\n");
/* Wait till we are at 1st instruction in prog. */
- signal = start_inferior (&argv[2], &status);
+ if (program_argv != NULL)
+ signal = start_inferior (program_argv, &status);
+ else
+ {
+ status = 'X';
+ signal = TARGET_SIGNAL_KILL;
+ }
goto restart;
- break;
}
else
{
@@ -1242,45 +1486,45 @@ main (int argc, char *argv[])
else
putpkt (own_buf);
- if (status == 'W')
- fprintf (stderr,
- "\nChild exited with status %d\n", signal);
- if (status == 'X')
- fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
- target_signal_to_host (signal),
- target_signal_to_name (signal));
- if (status == 'W' || status == 'X')
+ response_needed = 0;
+
+ if (was_running && (status == 'W' || status == 'X'))
{
- if (extended_protocol)
- {
- fprintf (stderr, "Killing inferior\n");
- kill_inferior ();
- write_ok (own_buf);
- fprintf (stderr, "GDBserver restarting\n");
+ was_running = 0;
- /* Wait till we are at 1st instruction in prog. */
- signal = start_inferior (&argv[2], &status);
- goto restart;
- break;
- }
+ if (status == 'W')
+ fprintf (stderr,
+ "\nChild exited with status %d\n", signal);
+ if (status == 'X')
+ fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n",
+ target_signal_to_host (signal),
+ target_signal_to_name (signal));
+
+ if (extended_protocol)
+ goto restart;
else
{
fprintf (stderr, "GDBserver exiting\n");
exit (0);
}
}
- }
- /* We come here when getpkt fails.
+ if (status != 'W' && status != 'X')
+ was_running = 1;
+ }
- For the extended remote protocol we exit (and this is the only
- way we gracefully exit!).
+ /* If an exit was requested (using the "monitor exit" command),
+ terminate now. The only other way to get here is for
+ getpkt to fail; close the connection and reopen it at the
+ top of the loop. */
- For the traditional remote protocol close the connection,
- and re-open it at the top of the loop. */
- if (extended_protocol)
+ if (exit_requested)
{
remote_close ();
+ if (attached && target_running ())
+ detach_inferior ();
+ else if (target_running ())
+ kill_inferior ();
exit (0);
}
else
Index: src/gdb/remote.c
===================================================================
--- src.orig/gdb/remote.c 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/remote.c 2007-12-07 15:05:13.000000000 -0500
@@ -237,6 +237,15 @@ struct remote_state
a buffer in the stub), this will be set to that packet size.
Otherwise zero, meaning to use the guessed size. */
long explicit_packet_size;
+
+ /* remote_wait is normally called when the target is running and
+ waits for a stop reply packet. But sometimes we need to call it
+ when the target is already stopped. We can send a "?" packet
+ and have remote_wait read the response. Or, if we already have
+ the response, we can stash it in BUF and tell remote_wait to
+ skip calling getpkt. This flag is set when BUF contains a
+ stop reply packet and the target is not waiting. */
+ int cached_wait_status;
};
/* This data could be associated with a target, but we do not always
@@ -514,6 +523,10 @@ static int remote_address_size;
static int remote_async_terminal_ours_p;
+/* The executable file to use for "run" on the remote side. */
+
+static char *remote_exec_file = "";
+
\f
/* User configurable variables for the number of characters in a
memory read/write packet. MIN (rsa->remote_packet_size,
@@ -920,6 +933,8 @@ enum {
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
+ PACKET_vAttach,
+ PACKET_vRun,
PACKET_MAX
};
@@ -1998,11 +2013,6 @@ extended_remote_restart (void)
putpkt (rs->buf);
remote_fileio_reset ();
-
- /* Now query for status so this looks just like we restarted
- gdbserver from scratch. */
- putpkt ("?");
- getpkt (&rs->buf, &rs->buf_size, 0);
}
\f
/* Clean up connection to a remote debugger. */
@@ -2164,27 +2174,79 @@ get_offsets (void)
/* Stub for catch_exception. */
+struct start_remote_args
+{
+ int from_tty;
+
+ /* The current target. */
+ struct target_ops *target;
+
+ /* Non-zero if this is an extended-remote target. */
+ int extended_p;
+};
+
static void
-remote_start_remote (struct ui_out *uiout, void *from_tty_p)
+remote_start_remote (struct ui_out *uiout, void *opaque)
{
- int from_tty = * (int *) from_tty_p;
+ struct remote_state *rs = get_remote_state ();
+ struct start_remote_args *args = opaque;
+ char *wait_status = NULL;
immediate_quit++; /* Allow user to interrupt it. */
/* Ack any packet which the remote side has already sent. */
serial_write (remote_desc, "+", 1);
+ /* Check whether the target is running now. */
+ putpkt ("?");
+ getpkt (&rs->buf, &rs->buf_size, 0);
+
+ if (rs->buf[0] == 'W' || rs->buf[0] == 'X')
+ {
+ if (args->extended_p)
+ {
+ /* We're connected, but not running. Drop out before we
+ call start_remote. */
+ target_mark_exited (args->target);
+ return;
+ }
+ else
+ error (_("The target is not running (try extended-remote?)"));
+ }
+ else
+ {
+ if (args->extended_p)
+ target_mark_running (args->target);
+
+ /* Save the reply for later. */
+ wait_status = alloca (strlen (rs->buf) + 1);
+ strcpy (wait_status, rs->buf);
+ }
+
/* Let the stub know that we want it to return the thread. */
set_thread (-1, 0);
+ /* Without this, some commands which require an active target
+ (such as kill) won't work. This variable serves (at least)
+ double duty as both the pid of the target process (if it has
+ such), and as a flag indicating that a target is active.
+ These functions should be split out into seperate variables,
+ especially since GDB will someday have a notion of debugging
+ several processes. */
+ inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+
+ /* Now, if we have thread information, update inferior_ptid. */
inferior_ptid = remote_current_thread (inferior_ptid);
get_offsets (); /* Get text, data & bss offsets. */
- putpkt ("?"); /* Initiate a query from remote machine. */
- immediate_quit--;
+ /* Use the previously fetched status. */
+ gdb_assert (wait_status != NULL);
+ strcpy (rs->buf, wait_status);
+ rs->cached_wait_status = 1;
- start_remote (from_tty); /* Initialize gdb process mechanisms. */
+ immediate_quit--;
+ start_remote (args->from_tty); /* Initialize gdb process mechanisms. */
}
/* Open a connection to a remote debugger.
@@ -2545,10 +2607,31 @@ remote_open_1 (char *name, int from_tty,
if (!async_p)
wait_forever_enabled_p = 1;
+ /* If we're connected to a running target, target_preopen will kill it.
+ But if we're connected to a target system with no running process,
+ then we will still be connected when it returns. Ask this question
+ first, before target_preopen has a chance to kill anything. */
+ if (remote_desc != NULL && !target_has_execution)
+ {
+ if (!from_tty
+ || query (_("Already connected to a remote target. Disconnect? ")))
+ pop_target ();
+ else
+ error (_("Still connected."));
+ }
+
target_preopen (from_tty);
unpush_target (target);
+ /* This time without a query. If we were connected to an
+ extended-remote target and target_preopen killed the running
+ process, we may still be connected. If we are starting "target
+ remote" now, the extended-remote target will not have been
+ removed by unpush_target. */
+ if (remote_desc != NULL && !target_has_execution)
+ pop_target ();
+
/* Make sure we send the passed signals list the next time we resume. */
xfree (last_pass_packet);
last_pass_packet = NULL;
@@ -2589,6 +2672,9 @@ remote_open_1 (char *name, int from_tty,
}
push_target (target); /* Switch to using remote target now. */
+ /* Assume that the target is running, unless we learn otherwise. */
+ target_mark_running (target);
+
/* Reset the target state; these things will be queried either by
remote_query_supported or as they are needed. */
init_all_packet_configs ();
@@ -2610,15 +2696,6 @@ remote_open_1 (char *name, int from_tty,
this before anything involving memory or registers. */
target_find_description ();
- /* Without this, some commands which require an active target (such
- as kill) won't work. This variable serves (at least) double duty
- as both the pid of the target process (if it has such), and as a
- flag indicating that a target is active. These functions should
- be split out into seperate variables, especially since GDB will
- someday have a notion of debugging several processes. */
-
- inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
-
if (async_p)
{
/* With this target we start out by owning the terminal. */
@@ -2653,9 +2730,14 @@ remote_open_1 (char *name, int from_tty,
all the ``target ....'' commands to share a common callback
function. See cli-dump.c. */
{
- struct gdb_exception ex
- = catch_exception (uiout, remote_start_remote, &from_tty,
- RETURN_MASK_ALL);
+ struct gdb_exception ex;
+ struct start_remote_args args;
+
+ args.from_tty = from_tty;
+ args.target = target;
+ args.extended_p = extended_p;
+
+ ex = catch_exception (uiout, remote_start_remote, &args, RETURN_MASK_ALL);
if (ex.reason < 0)
{
pop_target ();
@@ -2675,8 +2757,12 @@ remote_open_1 (char *name, int from_tty,
getpkt (&rs->buf, &rs->buf_size, 0);
}
- if (exec_bfd) /* No use without an exec file. */
- remote_check_symbols (symfile_objfile);
+ /* If we connected to a live target, do some additional setup. */
+ if (target_has_execution)
+ {
+ if (exec_bfd) /* No use without an exec file. */
+ remote_check_symbols (symfile_objfile);
+ }
}
/* This takes a program previously attached to and detaches it. After
@@ -2685,13 +2771,16 @@ remote_open_1 (char *name, int from_tty,
die when it hits one. */
static void
-remote_detach (char *args, int from_tty)
+remote_detach_1 (char *args, int from_tty, int extended)
{
struct remote_state *rs = get_remote_state ();
if (args)
error (_("Argument given to \"detach\" when remotely debugging."));
+ if (!target_has_execution)
+ error (_("No process to detach from."));
+
/* Tell the remote target to detach. */
strcpy (rs->buf, "D");
putpkt (rs->buf);
@@ -2706,7 +2795,24 @@ remote_detach (char *args, int from_tty)
target_mourn_inferior ();
if (from_tty)
- puts_filtered ("Ending remote debugging.\n");
+ {
+ if (extended)
+ puts_filtered ("Detached from remote process.\n");
+ else
+ puts_filtered ("Ending remote debugging.\n");
+ }
+}
+
+static void
+remote_detach (char *args, int from_tty)
+{
+ remote_detach_1 (args, from_tty, 0);
+}
+
+static void
+extended_remote_detach (char *args, int from_tty)
+{
+ remote_detach_1 (args, from_tty, 1);
}
/* Same as remote_detach, but don't send the "D" packet; just disconnect. */
@@ -2715,17 +2821,78 @@ static void
remote_disconnect (struct target_ops *target, char *args, int from_tty)
{
if (args)
- error (_("Argument given to \"detach\" when remotely debugging."));
+ error (_("Argument given to \"disconnect\" when remotely debugging."));
/* Unregister the file descriptor from the event loop. */
if (target_is_async_p ())
serial_async (remote_desc, NULL, 0);
- target_mourn_inferior ();
+ /* Make sure we unpush even the extended remote targets; mourn
+ won't do it. So call remote_mourn_1 directly instead of
+ target_mourn_inferior. */
+ remote_mourn_1 (target);
+
if (from_tty)
puts_filtered ("Ending remote debugging.\n");
}
+/* Attach to the process specified by ARGS. If FROM_TTY is non-zero,
+ be chatty about it. */
+
+static void
+extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
+{
+ struct remote_state *rs = get_remote_state ();
+ pid_t pid;
+ char *dummy;
+
+ if (!args)
+ error_no_arg (_("process-id to attach"));
+
+ dummy = args;
+ pid = strtol (args, &dummy, 0);
+ /* Some targets don't set errno on errors, grrr! */
+ if (pid == 0 && args == dummy)
+ error (_("Illegal process-id: %s."), args);
+
+ if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
+ error (_("This target does not support attaching to a process"));
+
+ sprintf (rs->buf, "vAttach;%x", pid);
+ putpkt (rs->buf);
+ getpkt (&rs->buf, &rs->buf_size, 0);
+
+ if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vAttach]) == PACKET_OK)
+ {
+ if (from_tty)
+ printf_unfiltered (_("Attached to %s\n"),
+ target_pid_to_str (pid_to_ptid (pid)));
+
+ /* We have a wait response; reuse it. */
+ rs->cached_wait_status = 1;
+ }
+ else if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE)
+ error (_("This target does not support attaching to a process"));
+ else
+ error (_("Attaching to %s failed"),
+ target_pid_to_str (pid_to_ptid (pid)));
+
+ target_mark_running (target);
+ inferior_ptid = pid_to_ptid (pid);
+}
+
+static void
+extended_remote_attach (char *args, int from_tty)
+{
+ extended_remote_attach_1 (&extended_remote_ops, args, from_tty);
+}
+
+static void
+extended_async_remote_attach (char *args, int from_tty)
+{
+ extended_remote_attach_1 (&extended_async_remote_ops, args, from_tty);
+}
+
/* Convert hex digit A to a number. */
static int
@@ -2850,7 +3017,7 @@ remote_vcont_resume (ptid_t ptid, int st
{
struct remote_state *rs = get_remote_state ();
int pid = PIDGET (ptid);
- char *buf = NULL, *outbuf;
+ char *outbuf;
struct cleanup *old_cleanup;
if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN)
@@ -3208,9 +3375,15 @@ remote_wait (ptid_t ptid, struct target_
{
char *buf, *p;
- ofunc = signal (SIGINT, remote_interrupt);
- getpkt (&rs->buf, &rs->buf_size, 1);
- signal (SIGINT, ofunc);
+ if (rs->cached_wait_status)
+ /* Use the cached wait status, but only once. */
+ rs->cached_wait_status = 0;
+ else
+ {
+ ofunc = signal (SIGINT, remote_interrupt);
+ getpkt (&rs->buf, &rs->buf_size, 1);
+ signal (SIGINT, ofunc);
+ }
buf = rs->buf;
@@ -3417,15 +3590,21 @@ remote_async_wait (ptid_t ptid, struct t
{
char *buf, *p;
- if (!target_is_async_p ())
- ofunc = signal (SIGINT, remote_interrupt);
- /* FIXME: cagney/1999-09-27: If we're in async mode we should
- _never_ wait for ever -> test on target_is_async_p().
- However, before we do that we need to ensure that the caller
- knows how to take the target into/out of async mode. */
- getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
- if (!target_is_async_p ())
- signal (SIGINT, ofunc);
+ if (rs->cached_wait_status)
+ /* Use the cached wait status, but only once. */
+ rs->cached_wait_status = 0;
+ else
+ {
+ if (!target_is_async_p ())
+ ofunc = signal (SIGINT, remote_interrupt);
+ /* FIXME: cagney/1999-09-27: If we're in async mode we should
+ _never_ wait for ever -> test on target_is_async_p().
+ However, before we do that we need to ensure that the caller
+ knows how to take the target into/out of async mode. */
+ getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p);
+ if (!target_is_async_p ())
+ signal (SIGINT, ofunc);
+ }
buf = rs->buf;
@@ -4694,6 +4873,7 @@ putpkt (char *buf)
static int
putpkt_binary (char *buf, int cnt)
{
+ struct remote_state *rs = get_remote_state ();
int i;
unsigned char csum = 0;
char *buf2 = alloca (cnt + 6);
@@ -4702,6 +4882,10 @@ putpkt_binary (char *buf, int cnt)
int tcount = 0;
char *p;
+ /* We're sending out a new packet. Make sure we don't look at a
+ stale cached response. */
+ rs->cached_wait_status = 0;
+
/* Copy the packet into buffer BUF2, encapsulating it
and giving it a checksum. */
@@ -5003,11 +5187,16 @@ getpkt (char **buf,
static int
getpkt_sane (char **buf, long *sizeof_buf, int forever)
{
+ struct remote_state *rs = get_remote_state ();
int c;
int tries;
int timeout;
int val;
+ /* We're reading a new response. Make sure we don't look at a
+ previously cached response. */
+ rs->cached_wait_status = 0;
+
strcpy (*buf, "timeout");
if (forever)
@@ -5139,19 +5328,6 @@ remote_async_mourn (void)
remote_mourn_1 (&remote_async_ops);
}
-static void
-extended_remote_mourn (void)
-{
- /* We do _not_ want to mourn the target like this; this will
- remove the extended remote target from the target stack,
- and the next time the user says "run" it'll fail.
-
- FIXME: What is the right thing to do here? */
-#if 0
- remote_mourn_1 (&extended_remote_ops);
-#endif
-}
-
/* Worker function for remote_mourn. */
static void
remote_mourn_1 (struct target_ops *target)
@@ -5160,71 +5336,167 @@ remote_mourn_1 (struct target_ops *targe
generic_mourn_inferior ();
}
-/* In the extended protocol we want to be able to do things like
- "run" and have them basically work as expected. So we need
- a special create_inferior function.
+static void
+extended_remote_mourn_1 (struct target_ops *target)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ /* Unlike "target remote", we do not want to unpush the target; then
+ the next time the user says "run", we won't be connected. */
+
+ /* Call common code to mark the inferior as not running. */
+ generic_mourn_inferior ();
+
+ /* Check whether the target is running now - some remote stubs
+ automatically restart after kill. */
+ putpkt ("?");
+ getpkt (&rs->buf, &rs->buf_size, 0);
+
+ if (rs->buf[0] == 'S' || rs->buf[0] == 'T')
+ {
+ /* Assume that the target has been restarted. Set inferior_ptid
+ so that bits of core GDB realizes there's something here, e.g.,
+ so that the user can say "kill" again. */
+ inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+ }
+ else
+ {
+ /* Mark this (still pushed) target as not executable until we
+ restart it. */
+ target_mark_exited (target);
+ }
+}
- FIXME: One day add support for changing the exec file
- we're debugging, arguments and an environment. */
+static void
+extended_remote_mourn (void)
+{
+ extended_remote_mourn_1 (&extended_remote_ops);
+}
static void
-extended_remote_create_inferior (char *exec_file, char *args,
- char **env, int from_tty)
+extended_async_remote_mourn (void)
+{
+ extended_remote_mourn_1 (&extended_async_remote_ops);
+}
+
+static int
+extended_remote_run (char *args)
{
- /* Rip out the breakpoints; we'll reinsert them after restarting
- the remote server. */
- remove_breakpoints ();
+ struct remote_state *rs = get_remote_state ();
+ char *p;
+ int len;
- /* Now restart the remote server. */
- extended_remote_restart ();
+ /* If the user has disabled vRun support, or we have detected that
+ support is not available, do not try it. */
+ if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE)
+ return -1;
- /* NOTE: We don't need to recheck for a target description here; but
- if we gain the ability to switch the remote executable we may
- need to, if for instance we are running a process which requested
- different emulated hardware from the operating system. A
- concrete example of this is ARM GNU/Linux, where some binaries
- will have a legacy FPA coprocessor emulated and others may have
- access to a hardware VFP unit. */
-
- /* Now put the breakpoints back in. This way we're safe if the
- restart function works via a unix fork on the remote side. */
- insert_breakpoints ();
+ strcpy (rs->buf, "vRun;");
+ len = strlen (rs->buf);
- /* Clean up from the last time we were running. */
- clear_proceed_status ();
+ if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ())
+ error (_("Remote file name too long for run packet"));
+ len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf + len, 0);
+
+ if (*args)
+ {
+ struct cleanup *back_to;
+ int i;
+ char **argv;
+
+ argv = buildargv (args);
+ back_to = make_cleanup ((void (*) (void *)) freeargv, argv);
+ for (i = 0; argv[i] != NULL; i++)
+ {
+ if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ())
+ error (_("Argument list too long for run packet"));
+ rs->buf[len++] = ';';
+ len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf + len, 0);
+ }
+ do_cleanups (back_to);
+ }
+
+ rs->buf[len++] = '\0';
+
+ putpkt (rs->buf);
+ getpkt (&rs->buf, &rs->buf_size, 0);
+
+ if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vRun]) == PACKET_OK)
+ {
+ /* We have a wait response; we don't need it, though. All is well. */
+ return 0;
+ }
+ else if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE)
+ /* It wasn't disabled before, but it is now. */
+ return -1;
+ else
+ {
+ if (remote_exec_file == NULL)
+ error (_("Running the default executable on the remote target failed; "
+ "try \"set remote exec-file\"?"));
+ else
+ error (_("Running \"%s\" on the remote target failed"),
+ remote_exec_file);
+ }
}
-/* Async version of extended_remote_create_inferior. */
+/* In the extended protocol we want to be able to do things like
+ "run" and have them basically work as expected. So we need
+ a special create_inferior function. We support changing the
+ executable file and the command line arguments, but not the
+ environment. */
+
static void
-extended_remote_async_create_inferior (char *exec_file, char *args,
- char **env, int from_tty)
+extended_remote_create_inferior_1 (char *exec_file, char *args,
+ char **env, int from_tty,
+ int async_p)
{
- /* Rip out the breakpoints; we'll reinsert them after restarting
- the remote server. */
- remove_breakpoints ();
-
/* If running asynchronously, register the target file descriptor
with the event loop. */
- if (target_can_async_p ())
+ if (async_p && target_can_async_p ())
target_async (inferior_event_handler, 0);
/* Now restart the remote server. */
- extended_remote_restart ();
+ if (extended_remote_run (args) == -1)
+ {
+ /* vRun was not supported. Fail if we need it to do what the
+ user requested. */
+ if (remote_exec_file[0])
+ error (_("Remote target does not support \"set remote exec-file\""));
+ if (args[0])
+ error (_("Remote target does not support \"set args\" or run <ARGS>"));
+
+ /* Fall back to "R". */
+ extended_remote_restart ();
+ }
+
+ /* Now mark the inferior as running before we do anything else. */
+ inferior_ptid = pid_to_ptid (MAGIC_NULL_PID);
+ if (async_p)
+ target_mark_running (&extended_async_remote_ops);
+ else
+ target_mark_running (&extended_remote_ops);
- /* NOTE: We don't need to recheck for a target description here; but
- if we gain the ability to switch the remote executable we may
- need to, if for instance we are running a process which requested
- different emulated hardware from the operating system. A
- concrete example of this is ARM GNU/Linux, where some binaries
- will have a legacy FPA coprocessor emulated and others may have
- access to a hardware VFP unit. */
-
- /* Now put the breakpoints back in. This way we're safe if the
- restart function works via a unix fork on the remote side. */
- insert_breakpoints ();
+ /* Get updated offsets, if the stub uses qOffsets. */
+ get_offsets ();
/* Clean up from the last time we were running. */
- clear_proceed_status ();
+ init_thread_list ();
+ init_wait_for_inferior ();
+}
+
+static void
+extended_remote_create_inferior (char *exec_file, char *args,
+ char **env, int from_tty)
+{
+ extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0);
+}
+
+static void
+extended_remote_async_create_inferior (char *exec_file, char *args,
+ char **env, int from_tty)
+{
+ extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1);
}
\f
@@ -5782,6 +6054,12 @@ remote_xfer_partial (struct target_ops *
int xfered;
errno = 0;
+ /* If the remote target is connected but not running, we should
+ pass this request down to a lower stratum (e.g. the executable
+ file). */
+ if (!target_has_execution)
+ return 0;
+
if (writebuf != NULL)
xfered = remote_write_bytes (offset, writebuf, len);
else
@@ -6982,6 +7260,8 @@ Specify the serial device it is connecte
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
+ extended_remote_ops.to_detach = extended_remote_detach;
+ extended_remote_ops.to_attach = extended_remote_attach;
}
static int
@@ -7114,7 +7394,9 @@ init_extended_async_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).",
extended_async_remote_ops.to_open = extended_remote_async_open;
extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior;
- extended_async_remote_ops.to_mourn_inferior = extended_remote_mourn;
+ extended_async_remote_ops.to_mourn_inferior = extended_async_remote_mourn;
+ extended_async_remote_ops.to_detach = extended_remote_detach;
+ extended_async_remote_ops.to_attach = extended_async_remote_attach;
}
static void
@@ -7369,6 +7651,12 @@ Show the maximum size of the address (in
add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink],
"vFile:unlink", "hostio-unlink", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_vAttach],
+ "vAttach", "attach", 0);
+
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_vRun],
+ "vRun", "run", 0);
+
/* Keep the old ``set remote Z-packet ...'' working. Each individual
Z sub-packet has its own set and show commands, but users may
have sets to this variable in their .gdbinit files (or in their
@@ -7401,6 +7689,13 @@ Transfer files to and from the remote ta
_("Delete a remote file."),
&remote_cmdlist);
+ remote_exec_file = xstrdup ("");
+ add_setshow_string_noescape_cmd ("exec-file", class_files,
+ &remote_exec_file, _("\
+Set the remote pathname for \"run\""), _("\
+Show the remote pathname for \"run\""), NULL, NULL, NULL,
+ &remote_set_cmdlist, &remote_show_cmdlist);
+
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist);
}
Index: src/gdb/testsuite/gdb.server/ext-attach.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-attach.c 2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,31 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2007 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/>. */
+
+/* This program is intended to be started outside of gdb, and then
+ attached to by gdb. It loops for a while, but not forever. */
+
+#include <unistd.h>
+
+int main ()
+{
+ int i;
+
+ for (i = 0; i < 120; i++)
+ sleep (1);
+
+ return 0;
+}
Index: src/gdb/testsuite/gdb.server/ext-attach.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-attach.exp 2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,77 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007 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/>.
+
+# Test attaching to already-running programs using extended-remote.
+
+load_lib gdbserver-support.exp
+
+set testfile "ext-attach"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [skip_gdbserver_tests] } {
+ return 0
+}
+
+# On SPU, this test currently fails because "sleep" is not supported.
+if { [istarget "spu*-*-*"] } {
+ return 0
+}
+
+# We need to use TCL's exec to get the pid.
+if [is_remote target] then {
+ return 0
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ untested ext-attach.exp
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load $binfile
+gdb_reinitialize_dir $srcdir/$subdir
+
+set target_exec [gdbserver_download]
+gdbserver_start_extended
+
+gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
+
+# Start the program running and then wait for a bit, to be sure
+# that it can be attached to.
+set testpid [eval exec $binfile &]
+exec sleep 2
+if { [istarget "*-*-cygwin*"] } {
+ # testpid is the Cygwin PID, GDB uses the Windows PID, which might be
+ # different due to the way fork/exec works.
+ set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ]
+}
+
+gdb_test "attach $testpid" "Attached to.*" \
+ "attach to remote program 1"
+gdb_test "backtrace" ".*main.*" "backtrace 1"
+
+gdb_test "detach" "Detached from remote process\\."
+gdb_test "backtrace" "No stack\\." "backtrace with no program"
+
+gdb_test "attach $testpid" "Attached to.*" \
+ "attach to remote program 2"
+gdb_test "backtrace" ".*main.*" "backtrace 2"
+
+gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
+gdb_test "monitor exit" ""
Index: src/gdb/testsuite/gdb.server/ext-run.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.server/ext-run.exp 2007-12-07 10:38:30.000000000 -0500
@@ -0,0 +1,48 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2007 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/>.
+
+# Test running programs using extended-remote.
+
+load_lib gdbserver-support.exp
+
+set testfile "server"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [skip_gdbserver_tests] } {
+ return 0
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load $binfile
+gdb_reinitialize_dir $srcdir/$subdir
+
+set target_exec [gdbserver_download]
+gdbserver_start_extended
+
+gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
+
+gdb_breakpoint main
+gdb_test "run" "Breakpoint.* main .*" "continue to main"
+
+gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
+gdb_test "monitor exit" ""
Index: src/gdb/testsuite/lib/gdbserver-support.exp
===================================================================
--- src.orig/gdb/testsuite/lib/gdbserver-support.exp 2007-12-07 10:37:55.000000000 -0500
+++ src/gdb/testsuite/lib/gdbserver-support.exp 2007-12-07 10:38:30.000000000 -0500
@@ -129,13 +129,10 @@ proc skip_gdbserver_tests { } {
return 0
}
-# Start a gdbserver process running SERVER_EXEC, and connect GDB
-# to it. CHILD_ARGS are passed to the inferior.
-#
-# Returns the target protocol and socket to connect to.
+# Download the currently loaded program to the target if necessary.
+# Return the target system filename.
-proc gdbserver_spawn { child_args } {
- global portnum
+proc gdbserver_download { } {
global gdbserver_host_exec
global gdbserver_host_mtime
global gdbserver_server_exec
@@ -169,6 +166,17 @@ proc gdbserver_spawn { child_args } {
}
}
+ return $gdbserver_server_exec
+}
+
+# Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS.
+# The port will be filled in between them automatically.
+#
+# Returns the target protocol and socket to connect to.
+
+proc gdbserver_start { options arguments } {
+ global portnum
+
# Port id -- either specified in baseboard file, or managed here.
if [target_info exists gdb,socketport] {
set portnum [target_info gdb,socketport]
@@ -179,7 +187,7 @@ proc gdbserver_spawn { child_args } {
# Extract the local and remote host ids from the target board struct.
if [target_info exists sockethost] {
- set debughost [target_info sockethost]
+ set debughost [target_info sockethost]
} else {
set debughost "localhost:"
}
@@ -196,23 +204,23 @@ proc gdbserver_spawn { child_args } {
# Export the host:port pair.
set gdbport $debughost$portnum
- # Fire off the debug agent. This flavour of gdbserver takes as
- # arguments the port information, the name of the executable file to
- # be debugged, and any arguments.
- set gdbserver_command "$gdbserver :$portnum $gdbserver_server_exec"
- if { $child_args != "" } {
- append gdbserver_command " $child_args"
+ # Fire off the debug agent.
+ set gdbserver_command "$gdbserver"
+ if { $options != "" } {
+ append gdbserver_command " $options"
+ }
+ append gdbserver_command " :$portnum"
+ if { $arguments != "" } {
+ append gdbserver_command " $arguments"
}
set server_spawn_id [remote_spawn target $gdbserver_command]
- # Wait for the server to produce at least one line and an additional
- # character of output. This will wait until any TCP socket has been
- # created, so that GDB can connect.
+ # Wait for the server to open its TCP socket, so that GDB can connect.
expect {
-i $server_spawn_id
-notransfer
- -re ".*\n." { }
+ -re "Listening on" { }
}
# We can't just call close, because if gdbserver is local then that means
@@ -231,6 +239,24 @@ proc gdbserver_spawn { child_args } {
return [list $protocol $gdbport]
}
+# Start a gdbserver process running SERVER_EXEC, and connect GDB
+# to it. CHILD_ARGS are passed to the inferior.
+#
+# Returns the target protocol and socket to connect to.
+
+proc gdbserver_spawn { child_args } {
+ set target_exec [gdbserver_download]
+
+ # Fire off the debug agent. This flavour of gdbserver takes as
+ # arguments the port information, the name of the executable file to
+ # be debugged, and any arguments.
+ set arguments "$target_exec"
+ if { $child_args != "" } {
+ append arguments " $child_args"
+ }
+ return [gdbserver_start "" $arguments]
+}
+
# Start a gdbserver process running HOST_EXEC and pass CHILD_ARGS
# to it. Return 0 on success, or non-zero on failure.
@@ -268,3 +294,12 @@ proc gdbserver_reconnect { } {
return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport]
}
+
+# Start and connect to a gdbserver in extended mode.
+proc gdbserver_start_extended { } {
+ set res [gdbserver_start "--multi" ""]
+ set gdbserver_protocol "extended-[lindex $res 0]"
+ set gdbserver_gdbport [lindex $res 1]
+
+ return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport]
+}
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS 2007-12-07 16:09:57.000000000 -0500
+++ src/gdb/NEWS 2007-12-07 16:13:30.000000000 -0500
@@ -35,6 +35,9 @@ targets even when the libthread_db libra
* The GDB remote stub, gdbserver, now supports the new file transfer
commands (remote put, remote get, and remote delete).
+* The GDB remote stub, gdbserver, now supports run and attach in
+extended-remote mode.
+
* hppa*64*-*-hpux11* target broken
The debugger is unable to start a program and fails with the following
error: "Error trying to get information about dynamic linker".
@@ -67,6 +70,13 @@ vFile:pwrite:
vFile:unlink:
Open, close, read, write, and delete files on the remote system.
+vAttach
+ Attach to an existing process on the remote system, in extended-remote
+ mode.
+
+vRun
+ Run a new process on the remote system, in extended-remote mode.
+
*** Changes in GDB 6.7
* Resolved 101 resource leaks, null pointer dereferences, etc. in gdb,
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [rfc] Multiple process support in gdbserver 2007-12-08 10:54 [rfc] Multiple process support in gdbserver Daniel Jacobowitz @ 2007-12-08 15:29 ` Eli Zaretskii 2007-12-08 18:14 ` Daniel Jacobowitz 0 siblings, 1 reply; 11+ messages in thread From: Eli Zaretskii @ 2007-12-08 15:29 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: gdb-patches > Date: Fri, 7 Dec 2007 16:23:52 -0500 > From: Daniel Jacobowitz <drow@false.org> > > +@subsubsection Multi-Process Mode for @code{gdbserver} This subsubsection could benefit from a @cindex entry, since it describes an important functionality. > -was compiled with the correct sysroot using @code{--with-system-root}). > +was compiled with the correct sysroot using @code{--with-sysroot}). Does this mean the previous wording was just plain wrong, or are you changing the option's spelling? > +@item monitor exit > +Tell gdbserver to exit immediately. This command should be followed by > +@code{disconnect} to close the debugging session. @code{gdbserver} will > +detach from any attached processes and kill any processes it created. A short explanation how this is used in multi-process debugging would be good here. > +This packet is only available in extended mode. Suggest a cross-reference to where extended mode is explained. > @@ -24775,7 +24877,7 @@ return -1 if an error occurs. @var{path > @var{flags} is an integer indicating a mask of open flags > (@pxref{Open Flags}), and @var{mode} is an integer indicating a mask > of mode bits to use if the file is created (@pxref{mode_t Values}). > -@xref{open} for details of the open flags and mode values. > +@ref{open} for details of the open flags and mode values. @ref does not produce a "see", so the above is wrong. Why did you replace @xref in the first place, it looks allright here (except that you need a comma after the right brace)? > +* The GDB remote stub, gdbserver, now supports run and attach in > +extended-remote mode. Is "extended-remote mode" a known term? If not, we should probably explain it, or else replace it with a more plain-language description of it. Other that these comments, the documentation patches are approved. Thanks. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2007-12-08 15:29 ` Eli Zaretskii @ 2007-12-08 18:14 ` Daniel Jacobowitz 2007-12-10 15:34 ` Daniel Jacobowitz 0 siblings, 1 reply; 11+ messages in thread From: Daniel Jacobowitz @ 2007-12-08 18:14 UTC (permalink / raw) To: gdb-patches On Sat, Dec 08, 2007 at 01:41:40PM +0200, Eli Zaretskii wrote: > > -was compiled with the correct sysroot using @code{--with-system-root}). > > +was compiled with the correct sysroot using @code{--with-sysroot}). > > Does this mean the previous wording was just plain wrong, or are you > changing the option's spelling? It was just wrong. > @ref does not produce a "see", so the above is wrong. Why did you > replace @xref in the first place, it looks allright here (except that > you need a comma after the right brace)? Just trying to fix the info build; I'll add the comma instead. > > +* The GDB remote stub, gdbserver, now supports run and attach in > > +extended-remote mode. > > Is "extended-remote mode" a known term? If not, we should probably > explain it, or else replace it with a more plain-language description > of it. I think it is a sufficiently known term; it corresponds to "target extended-remote", which is documented. Thanks for the comments! I will update the patch (next week). -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2007-12-08 18:14 ` Daniel Jacobowitz @ 2007-12-10 15:34 ` Daniel Jacobowitz 2008-01-24 1:50 ` Pedro Alves 0 siblings, 1 reply; 11+ messages in thread From: Daniel Jacobowitz @ 2007-12-10 15:34 UTC (permalink / raw) To: gdb-patches On Sat, Dec 08, 2007 at 10:29:35AM -0500, Daniel Jacobowitz wrote: > Thanks for the comments! I will update the patch (next week). Like so. -- Daniel Jacobowitz CodeSourcery 2007-12-07 Daniel Jacobowitz <dan@codesourcery.com> * linux-low.c (linux_attach_lwp): Do not _exit after errors. (linux_kill, linux_detach): Clean up the process list. * remote-utils.c (remote_open): Improve port number parsing. (putpkt_binary, input_interrupt): Only send interrupts if the target is running. * server.c (extended_protocol): Make static. (attached): Define earlier. (exit_requested, response_needed, program_argv): New variables. (target_running): New. (start_inferior): Clear attached here. (attach_inferior): Set attached here. (require_running): Define. (handle_query): Use require_running and target_running. Implement "monitor exit". (handle_v_attach, handle_v_run): New. (handle_v_requests): Use require_running. Handle vAttach and vRun. (gdbserver_usage): Update. (main): Redo argument parsing. Handle --debug and --multi. Handle --attach along with other options or after the port. Save program_argv. Support no initial program. Resynchronize communication with GDB after an error. Handle "monitor exit". Use require_running and target_running. Always allow the extended protocol. Do not error out for Hc0 or Hc-1. Do not automatically restart in extended mode. * README: Refer to the GDB manual. Update --attach usage. 2007-12-07 Daniel Jacobowitz <dan@codesourcery.com> * remote.c (struct remote_state): Add cached_wait_status. (remote_exec_file): New variable. (PACKET_vAttach, PACKET_vRun): New constants. (extended_remote_restart): Do not query for status. (struct start_remote_args): New. (remote_start_remote): Take it as a second argument. Check whether the target is running. Issue an error for non-running non-extended targets. Cache the wait status. Set inferior_ptid here. (remote_open_1): Prompt to disconnect non-running targets. Make sure the target is marked running. Do not set inferior_ptid here. Update call to remote_start_remote. Do not call remote_check_symbols if the target is not running. (remote_detach_1): Rename from remote_detach. Take an EXTENDED argument. Handle a non-running target. (remote_detach): Use it. (extended_remote_detach): New. (remote_disconnect): Fix typo. Use remoute_mourn_1. (extended_remote_attach_1, extended_remote_attach) (extended_async_remote_attach): New. (remote_vcont_resume): Remove unused variable. (remote_wait, remote_async_wait): Use any cached wait status. (putpkt_binary, getpkt): Clear any cached wait status. (extended_remoute_mourn_1): New. (extended_remote_mourn): Use it. (extended_async_remote_mourn, extended_remote_run): New. (extended_remote_create_inferior_1): New. (extended_remote_create_inferior): Use it. (extended_remote_async_create_inferior): Likewise. (remote_xfer_partial): Skip for non-executing targets. (init_extended_remote_ops): Set to_detach and to_attach. (init_extended_async_remote_ops): Likewise. Use extended_async_remote_mourn. (_initialize_remote): Register vAttach, vRun, and set remote exec-file. * NEWS: Mention vAttach, vRun, and gdbserver extended-remote support. 2007-12-07 Daniel Jacobowitz <dan@codesourcery.com> * gdb.server/ext-attach.c, gdb.server/ext-attach.exp, gdb.server/ext-run.exp: New files. * lib/gdbserver-support.exp (gdbserver_download): New. (gdbserver_start): New. Update gdbserver expected output. (gdbserver_spawn): Use them. (gdbserver_start_extended): New. 2007-12-07 Daniel Jacobowitz <dan@codesourcery.com> * gdb.texinfo (Using the `gdbserver' Program): Add security warning. Rearrange into subsections and subsubsections. Document --multi and --debug. Correct --with-sysroot typo. Update --attach usage. Make load reference clearer. Document monitor exit. (Remote Configuration): Document set remote exec-file, attach-packet, and run-packet. (Packets): Document vAttach and vRun. Correct syntax error. --- gdb/NEWS | 10 gdb/doc/gdb.texinfo | 137 +++++++- gdb/gdbserver/README | 4 gdb/gdbserver/linux-low.c | 24 + gdb/gdbserver/remote-utils.c | 12 gdb/gdbserver/server.c | 472 +++++++++++++++++++++++------- gdb/remote.c | 497 +++++++++++++++++++++++++------- gdb/testsuite/gdb.server/ext-attach.c | 31 + gdb/testsuite/gdb.server/ext-attach.exp | 77 ++++ gdb/testsuite/gdb.server/ext-run.exp | 48 +++ gdb/testsuite/lib/gdbserver-support.exp | 69 +++- 11 files changed, 1120 insertions(+), 261 deletions(-) Index: src/gdb/doc/gdb.texinfo =================================================================== --- src.orig/gdb/doc/gdb.texinfo 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/doc/gdb.texinfo 2007-12-10 10:29:42.000000000 -0500 @@ -12877,9 +12877,19 @@ choice for debugging. or a TCP connection, using the standard @value{GDBN} remote serial protocol. -@table @emph -@item On the target machine, -you need to have a copy of the program you want to debug. +@quotation +@emph{Warning:} @code{gdbserver} does not have any built-in security. +Do not run @code{gdbserver} connected to any public network; a +@value{GDBN} connection to @code{gdbserver} provides access to the +target system with the same privileges as the user running +@code{gdbserver}. +@end quotation + +@subsection Running @code{gdbserver} +@cindex arguments, to @code{gdbserver} + +Run @code{gdbserver} on the target system. You need a copy of the +program you want to debug, including any libraries it requires. @code{gdbserver} does not need your program's symbol table, so you can strip the program if necessary to save space. @value{GDBN} on the host system does all the symbol handling. @@ -12922,11 +12932,13 @@ conflicts with another service, @code{gd and exits.} You must use the same port number with the host @value{GDBN} @code{target remote} command. +@subsubsection Attaching to a Running Program + On some targets, @code{gdbserver} can also attach to running programs. This is accomplished via the @code{--attach} argument. The syntax is: @smallexample -target> gdbserver @var{comm} --attach @var{pid} +target> gdbserver --attach @var{comm} @var{pid} @end smallexample @var{pid} is the process ID of a currently running process. It isn't necessary @@ -12938,18 +12950,56 @@ You can debug processes by name instead @code{pidof} utility: @smallexample -target> gdbserver @var{comm} --attach `pidof @var{program}` +target> gdbserver --attach @var{comm} `pidof @var{program}` @end smallexample In case more than one copy of @var{program} is running, or @var{program} has multiple threads, most versions of @code{pidof} support the @code{-s} option to only return the first process ID. -@item On the host machine, -first make sure you have the necessary symbol files. Load symbols for +@subsubsection Multi-Process Mode for @code{gdbserver} +@cindex gdbserver, multiple processes +@cindex multiple processes with gdbserver + +When you connect to @code{gdbserver} using @code{target remote}, +@code{gdbserver} debugs the specified program only once. When the +program exits, or you detach from it, @value{GDBN} closes the connection +and @code{gdbserver} exits. + +If you connect using @code{target extended-remote}, @code{gdbserver} +enters multi-process mode. When the debugged program exits, or you +detach from it, @value{GDBN} stays connected to @code{gdbserver} even +though no program is running. The @code{run} and @code{attach} +commands instruct @code{gdbserver} to run or attach to a new program. +The @code{run} command uses @code{set remote exec-file} (@pxref{set +remote exec-file}) to select the program to run. Command line +arguments are supported, except for wildcard expansion and I/O +redirection (@pxref{Arguments}). + +To start @code{gdbserver} without supplying an initial command to run +or process ID to attach, use the @option{--multi} command line option. +Then you can connect using @code{target extended-remote} and start +the program you want to debug. + +@code{gdbserver} does not automatically exit in multi-process mode. +You can terminate it by using @code{monitor exit} +(@pxref{Monitor Commands for gdbserver}). + +@subsubsection Other Command-Line Arguments for @code{gdbserver} + +You can include @option{--debug} on the @code{gdbserver} command line. +@code{gdbserver} will display extra status information about the debugging +process. This option is intended for @code{gdbserver} development and +for bug reports to the developers. + +@subsection Connecting to @code{gdbserver} + +Run @value{GDBN} on the host system. + +First make sure you have the necessary symbol files. Load symbols for your application using the @code{file} command before you connect. Use @code{set sysroot} to locate target libraries (unless your @value{GDBN} -was compiled with the correct sysroot using @code{--with-system-root}). +was compiled with the correct sysroot using @code{--with-sysroot}). The symbol file and target libraries must exactly match the executable and libraries on the target, with one exception: the files on the host @@ -12963,19 +13013,17 @@ Connect to your target (@pxref{Connectin For TCP connections, you must start up @code{gdbserver} prior to using the @code{target remote} command. Otherwise you may get an error whose text depends on the host system, but which usually looks something like -@samp{Connection refused}. You don't need to use the @code{load} +@samp{Connection refused}. Don't use the @code{load} command in @value{GDBN} when using @code{gdbserver}, since the program is already on the target. -@end table - @subsection Monitor Commands for @code{gdbserver} @cindex monitor commands, for @code{gdbserver} +@anchor{Monitor Commands for gdbserver} During a @value{GDBN} session using @code{gdbserver}, you can use the @code{monitor} command to send special requests to @code{gdbserver}. -Here are the available commands; they are only of interest when -debugging @value{GDBN} or @code{gdbserver}. +Here are the available commands. @table @code @item monitor help @@ -12990,6 +13038,13 @@ Disable or enable general debugging mess Disable or enable specific debugging messages associated with the remote protocol (@pxref{Remote Protocol}). +@item monitor exit +Tell gdbserver to exit immediately. This command should be followed by +@code{disconnect} to close the debugging session. @code{gdbserver} will +detach from any attached processes and kill any processes it created. +Use @code{monitor exit} to terminate @code{gdbserver} at the end +of a multi-process mode debug session. + @end table @node Remote Configuration @@ -13084,6 +13139,15 @@ responses. @itemx set remote hardware-breakpoint-limit @var{limit} Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or watchpoints. A limit of -1, the default, is treated as unlimited. + +@item set remote exec-file @var{filename} +@itemx show remote exec-file +@anchor{set remote exec-file} +@cindex executable file, for remote target +Select the file used for @code{run} with @code{target +extended-remote}. This should be set to a filename valid on the +target system. If it is not set, the target will use a default +filename (e.g.@: the last program run). @end table @cindex remote packets, enabling and disabling @@ -13130,10 +13194,18 @@ are: @tab @code{qSymbol} @tab Detecting multiple threads +@item @code{attach} +@tab @code{vAttach} +@tab @code{attach} + @item @code{verbose-resume} @tab @code{vCont} @tab Stepping or resuming multiple threads +@item @code{run} +@tab @code{vRun} +@tab @code{run} + @item @code{software-breakpoint} @tab @code{Z0} @tab @code{break} @@ -23212,6 +23284,7 @@ Here are the packet descriptions. @item ! @cindex @samp{!} packet +@anchor{extended mode} Enable extended mode. In extended mode, the remote server is made persistent. The @samp{R} packet is used to restart the program being debugged. @@ -23477,7 +23550,7 @@ Don't use this packet; use the @samp{R} @item R @var{XX} @cindex @samp{R} packet Restart the program being debugged. @var{XX}, while needed, is ignored. -This packet is only available in extended mode. +This packet is only available in extended mode (@pxref{extended mode}). The @samp{R} packet has no reply. @@ -23520,6 +23593,22 @@ thread is dead Packets starting with @samp{v} are identified by a multi-letter name, up to the first @samp{;} or @samp{?} (or the end of the packet). +@item vAttach;@var{pid} +@cindex @samp{vAttach} packet +Attach to a new process with the specified process ID. @var{pid} is a +hexadecimal integer identifying the process. If the stub is currently +controlling a process, it is killed. The attached process is stopped. + +This packet is only available in extended mode (@pxref{extended mode}). + +Reply: +@table @samp +@item E @var{nn} +for an error +@item @r{Any stop packet} +for success (@pxref{Stop Reply Packets}) +@end table + @item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{} @cindex @samp{vCont} packet Resume the inferior, specifying different actions for each thread. @@ -23616,6 +23705,24 @@ The stub is permitted to delay or batch regions of flash memory are unpredictable until the @samp{vFlashDone} request is completed. +@item vRun;@var{filename}@r{[};@var{argument}@r{]}@dots{} +@cindex @samp{vRun} packet +Run the program @var{filename}, passing it each @var{argument} on its +command line. The file and arguments are hex-encoded strings. If +@var{filename} is an empty string, the stub may use a default program +(e.g.@: the last program run). The program is created in the stopped +state. If the stub is currently controlling a process, it is killed. + +This packet is only available in extended mode (@pxref{extended mode}). + +Reply: +@table @samp +@item E @var{nn} +for an error +@item @r{Any stop packet} +for success (@pxref{Stop Reply Packets}) +@end table + @item X @var{addr},@var{length}:@var{XX@dots{}} @anchor{X packet} @cindex @samp{X} packet @@ -24775,7 +24882,7 @@ return -1 if an error occurs. @var{path @var{flags} is an integer indicating a mask of open flags (@pxref{Open Flags}), and @var{mode} is an integer indicating a mask of mode bits to use if the file is created (@pxref{mode_t Values}). -@xref{open} for details of the open flags and mode values. +@xref{open}, for details of the open flags and mode values. @item vFile:close: @var{fd} Close the open file corresponding to @var{fd} and return 0, or Index: src/gdb/gdbserver/README =================================================================== --- src.orig/gdb/gdbserver/README 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/gdbserver/README 2007-12-07 10:38:30.000000000 -0500 @@ -9,6 +9,8 @@ host. GDB and GDBserver communicate usi implemented in remote.c, and various *-stub.c files. They communicate via either a serial line or a TCP connection. +For more information about GDBserver, see the GDB manual. + Usage (server (target) side): First, you need to have a copy of the program you want to debug put onto @@ -47,7 +49,7 @@ print an error message and exit. On some targets, gdbserver can also attach to running programs. This is accomplished via the --attach argument. The syntax is: - target> gdbserver COMM --attach PID + target> gdbserver --attach COMM PID PID is the process ID of a currently running process. It isn't necessary to point gdbserver at a binary for the running process. Index: src/gdb/gdbserver/linux-low.c =================================================================== --- src.orig/gdb/gdbserver/linux-low.c 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/gdbserver/linux-low.c 2007-12-07 10:38:30.000000000 -0500 @@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid) if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) { - fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + if (all_threads.head != NULL) + { + /* If we fail to attach to an LWP, just warn. */ + fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + strerror (errno), errno); + fflush (stderr); + return; + } + else + /* If we fail to attach to a process, report an error. */ + error ("Cannot attach to process %ld: %s (%d)\n", pid, strerror (errno), errno); - fflush (stderr); - - /* If we fail to attach to an LWP, just return. */ - if (all_threads.head == NULL) - _exit (0177); - return; } ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE); @@ -396,6 +400,10 @@ linux_kill (void) /* Make sure it died. The loop is most likely unnecessary. */ wstat = linux_wait_for_event (thread); } while (WIFSTOPPED (wstat)); + + clear_inferiors (); + free (all_processes.head); + all_processes.head = all_processes.tail = NULL; } static void @@ -434,6 +442,8 @@ linux_detach (void) delete_all_breakpoints (); for_each_inferior (&all_threads, linux_detach_one_process); clear_inferiors (); + free (all_processes.head); + all_processes.head = all_processes.tail = NULL; return 0; } Index: src/gdb/gdbserver/remote-utils.c =================================================================== --- src.orig/gdb/gdbserver/remote-utils.c 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/gdbserver/remote-utils.c 2007-12-07 10:38:30.000000000 -0500 @@ -186,15 +186,15 @@ remote_open (char *name) #ifdef USE_WIN32API static int winsock_initialized; #endif - char *port_str; int port; struct sockaddr_in sockaddr; socklen_t tmp; int tmp_desc; + char *port_end; - port_str = strchr (name, ':'); - - port = atoi (port_str + 1); + port = strtoul (port_str + 1, &port_end, 10); + if (port_str[1] == '\0' || *port_end != '\0') + fatal ("Bad port argument: %s", name); #ifdef USE_WIN32API if (!winsock_initialized) @@ -574,7 +574,7 @@ putpkt_binary (char *buf, int cnt) } /* Check for an input interrupt while we're here. */ - if (buf3[0] == '\003') + if (buf3[0] == '\003' && current_inferior != NULL) (*the_target->request_interrupt) (); } while (buf3[0] != '+'); @@ -616,7 +616,7 @@ input_interrupt (int unused) cc = read (remote_desc, &c, 1); - if (cc != 1 || c != '\003') + if (cc != 1 || c != '\003' || current_inferior == NULL) { fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n", cc, c, c); Index: src/gdb/gdbserver/server.c =================================================================== --- src.orig/gdb/gdbserver/server.c 2007-12-07 10:38:17.000000000 -0500 +++ src/gdb/gdbserver/server.c 2007-12-07 14:26:13.000000000 -0500 @@ -34,9 +34,15 @@ unsigned long general_thread; unsigned long step_thread; unsigned long thread_from_wait; unsigned long old_thread_from_wait; -int extended_protocol; int server_waiting; +static int extended_protocol; +static int attached; +static int response_needed; +static int exit_requested; + +static char **program_argv; + /* Enable miscellaneous debugging output. The name is historical - it was originally used to debug LinuxThreads support. */ int debug_threads; @@ -69,8 +75,16 @@ restore_old_foreground_pgrp (void) #endif static int +target_running (void) +{ + return all_threads.head != NULL; +} + +static int start_inferior (char *argv[], char *statusptr) { + attached = 0; + #ifdef SIGTTOU signal (SIGTTOU, SIG_DFL); signal (SIGTTIN, SIG_DFL); @@ -107,6 +121,8 @@ attach_inferior (int pid, char *statuspt if (myattach (pid) != 0) return -1; + attached = 1; + fprintf (stderr, "Attached; pid = %d\n", pid); fflush (stderr); @@ -254,6 +270,13 @@ monitor_show_help (void) monitor_output (" Enable remote protocol debugging messages\n"); } +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + return; \ + } + /* Handle all of the extended 'q' packets. */ void handle_query (char *own_buf, int packet_len, int *new_packet_len_p) @@ -263,6 +286,7 @@ handle_query (char *own_buf, int packet_ /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0) { + require_running (own_buf); thread_ptr = all_threads.head; sprintf (own_buf, "QC%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); @@ -271,7 +295,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qSymbol::", own_buf) == 0) { - if (the_target->look_up_symbols != NULL) + if (target_running () && the_target->look_up_symbols != NULL) (*the_target->look_up_symbols) (); strcpy (own_buf, "OK"); @@ -280,6 +304,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qfThreadInfo", own_buf) == 0) { + require_running (own_buf); thread_ptr = all_threads.head; sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; @@ -288,6 +313,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qsThreadInfo", own_buf) == 0) { + require_running (own_buf); if (thread_ptr != NULL) { sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); @@ -305,7 +331,8 @@ handle_query (char *own_buf, int packet_ && strcmp ("qOffsets", own_buf) == 0) { CORE_ADDR text, data; - + + require_running (own_buf); if (the_target->read_offsets (&text, &data)) sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", (long)text, (long)data, (long)data); @@ -324,6 +351,7 @@ handle_query (char *own_buf, int packet_ CORE_ADDR ofs; unsigned char *spu_buf; + require_running (own_buf); strcpy (own_buf, "E00"); if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0) return; @@ -356,6 +384,7 @@ handle_query (char *own_buf, int packet_ CORE_ADDR ofs; unsigned char *spu_buf; + require_running (own_buf); strcpy (own_buf, "E00"); spu_buf = malloc (packet_len - 15); if (!spu_buf) @@ -387,6 +416,8 @@ handle_query (char *own_buf, int packet_ unsigned int len; char *annex; + require_running (own_buf); + /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0 || annex[0] != '\0') @@ -420,6 +451,8 @@ handle_query (char *own_buf, int packet_ const char *document; char *annex; + require_running (own_buf); + /* Check for support. */ document = get_features_xml ("target.xml"); if (document == NULL) @@ -467,6 +500,8 @@ handle_query (char *own_buf, int packet_ struct inferior_list_entry *dll_ptr; char *annex; + require_running (own_buf); + /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0 || annex[0] != '\0') @@ -535,7 +570,7 @@ handle_query (char *own_buf, int packet_ if (the_target->read_auxv != NULL) strcat (own_buf, ";qXfer:auxv:read+"); - + if (the_target->qxfer_spu != NULL) strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+"); @@ -553,6 +588,8 @@ handle_query (char *own_buf, int packet_ CORE_ADDR parts[3], address = 0; int i, err; + require_running (own_buf); + for (i = 0; i < 3; i++) { char *p2; @@ -642,6 +679,8 @@ handle_query (char *own_buf, int packet_ } else if (strcmp (mon, "help") == 0) monitor_show_help (); + else if (strcmp (mon, "exit") == 0) + exit_requested = 1; else { monitor_output ("Unknown monitor command.\n\n"); @@ -772,6 +811,95 @@ err: return; } +/* Attach to a new program. Return 1 if successful, 0 if failure. */ +int +handle_v_attach (char *own_buf, char *status, int *signal) +{ + int pid; + + pid = strtol (own_buf + 8, NULL, 16); + if (pid != 0 && attach_inferior (pid, status, signal) == 0) + { + prepare_resume_reply (own_buf, *status, *signal); + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + +/* Run a new program. Return 1 if successful, 0 if failure. */ +static int +handle_v_run (char *own_buf, char *status, int *signal) +{ + char *p, **pp, *next_p, **new_argv; + int i, new_argc; + + new_argc = 0; + for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';')) + { + p++; + new_argc++; + } + + new_argv = malloc ((new_argc + 2) * sizeof (char *)); + i = 0; + for (p = own_buf + strlen ("vRun;"); *p; p = next_p) + { + next_p = strchr (p, ';'); + if (next_p == NULL) + next_p = p + strlen (p); + + if (i == 0 && p == next_p) + new_argv[i] = NULL; + else + { + new_argv[i] = malloc (1 + (next_p - p) / 2); + unhexify (new_argv[i], p, (next_p - p) / 2); + new_argv[i][(next_p - p) / 2] = '\0'; + } + + if (*next_p) + next_p++; + i++; + } + new_argv[i] = NULL; + + if (new_argv[0] == NULL) + { + if (program_argv == NULL) + { + write_enn (own_buf); + return 0; + } + + new_argv[0] = strdup (program_argv[0]); + } + + /* Free the old argv. */ + if (program_argv) + { + for (pp = program_argv; *pp != NULL; pp++) + free (*pp); + free (program_argv); + } + program_argv = new_argv; + + *signal = start_inferior (program_argv, status); + if (*status == 'T') + { + prepare_resume_reply (own_buf, *status, *signal); + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + /* Handle all of the extended 'v' packets. */ void handle_v_requests (char *own_buf, char *status, int *signal, @@ -779,6 +907,7 @@ handle_v_requests (char *own_buf, char * { if (strncmp (own_buf, "vCont;", 6) == 0) { + require_running (own_buf); handle_v_cont (own_buf, status, signal); return; } @@ -793,6 +922,28 @@ handle_v_requests (char *own_buf, char * && handle_vFile (own_buf, packet_len, new_packet_len)) return; + if (strncmp (own_buf, "vAttach;", 8) == 0) + { + if (target_running ()) + { + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + } + handle_v_attach (own_buf, status, signal); + return; + } + + if (strncmp (own_buf, "vRun;", 5) == 0) + { + if (target_running ()) + { + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + } + handle_v_run (own_buf, status, signal); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -829,8 +980,6 @@ myresume (char *own_buf, int step, int * disable_async_io (); } -static int attached; - static void gdbserver_version (void) { @@ -844,13 +993,25 @@ gdbserver_version (void) static void gdbserver_usage (void) { - printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" - "\tgdbserver COMM --attach PID\n" + printf ("Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n" + "\tgdbserver [OPTIONS] --attach COMM PID\n" + "\tgdbserver [OPTIONS] --multi COMM\n" "\n" "COMM may either be a tty device (for serial debugging), or \n" - "HOST:PORT to listen for a TCP connection.\n"); + "HOST:PORT to listen for a TCP connection.\n" + "\n" + "Options:\n" + " --debug\t\tEnable debugging output.\n"); } +#undef require_running +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + break; \ + } + int main (int argc, char *argv[]) { @@ -862,18 +1023,38 @@ main (int argc, char *argv[]) CORE_ADDR mem_addr; int bad_attach; int pid; - char *arg_end; + char *arg_end, *port; + char **next_arg = &argv[1]; + int multi_mode = 0; + int attach = 0; + int was_running; - if (argc >= 2 && strcmp (argv[1], "--version") == 0) + while (*next_arg != NULL && **next_arg == '-') { - gdbserver_version (); - exit (0); - } + if (strcmp (*next_arg, "--version") == 0) + { + gdbserver_version (); + exit (0); + } + else if (strcmp (*next_arg, "--help") == 0) + { + gdbserver_usage (); + exit (0); + } + else if (strcmp (*next_arg, "--attach") == 0) + attach = 1; + else if (strcmp (*next_arg, "--multi") == 0) + multi_mode = 1; + else if (strcmp (*next_arg, "--debug") == 0) + debug_threads = 1; + else + { + fprintf (stderr, "Unknown argument: %s\n", *next_arg); + exit (1); + } - if (argc >= 2 && strcmp (argv[1], "--help") == 0) - { - gdbserver_usage (); - exit (0); + next_arg++; + continue; } if (setjmp (toplevel)) @@ -882,23 +1063,34 @@ main (int argc, char *argv[]) exit (1); } - bad_attach = 0; - pid = 0; - attached = 0; - if (argc >= 3 && strcmp (argv[2], "--attach") == 0) + port = *next_arg; + next_arg++; + if (port == NULL || (!attach && !multi_mode && *next_arg == NULL)) { - if (argc == 4 - && argv[3][0] != '\0' - && (pid = strtoul (argv[3], &arg_end, 0)) != 0 - && *arg_end == '\0') - { - ; - } - else - bad_attach = 1; + gdbserver_usage (); + exit (1); } - if (argc < 3 || bad_attach) + bad_attach = 0; + pid = 0; + + /* --attach used to come after PORT, so allow it there for + compatibility. */ + if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0) + { + attach = 1; + next_arg++; + } + + if (attach + && (*next_arg == NULL + || (*next_arg)[0] == '\0' + || (pid = strtoul (*next_arg, &arg_end, 0)) == 0 + || *arg_end != '\0' + || next_arg[1] != NULL)) + bad_attach = 1; + + if (bad_attach) { gdbserver_usage (); exit (1); @@ -910,26 +1102,34 @@ main (int argc, char *argv[]) own_buf = malloc (PBUFSIZ + 1); mem_buf = malloc (PBUFSIZ); - if (pid == 0) + if (pid == 0 && *next_arg != NULL) { + int i, n; + + n = argc - (next_arg - argv); + program_argv = malloc (sizeof (char *) * (n + 1)); + for (i = 0; i < n; i++) + program_argv[i] = strdup (next_arg[i]); + program_argv[i] = NULL; + /* Wait till we are at first instruction in program. */ - signal = start_inferior (&argv[2], &status); + signal = start_inferior (program_argv, &status); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was successfully created. */ } + else if (pid != 0) + { + if (attach_inferior (pid, &status, &signal) == -1) + error ("Attaching not supported on this target"); + + /* Otherwise succeeded. */ + } else { - switch (attach_inferior (pid, &status, &signal)) - { - case -1: - error ("Attaching not supported on this target"); - break; - default: - attached = 1; - break; - } + status = 'W'; + signal = 0; } /* Don't report shared library events on the initial connection, @@ -945,27 +1145,43 @@ main (int argc, char *argv[]) } if (status == 'W' || status == 'X') + was_running = 0; + else + was_running = 1; + + if (!was_running && !multi_mode) { - fprintf (stderr, "No inferior, GDBserver exiting.\n"); + fprintf (stderr, "No program to debug. GDBserver exiting.\n"); exit (1); } while (1) { - remote_open (argv[1]); + remote_open (port); restart: - setjmp (toplevel); + if (setjmp (toplevel) != 0) + { + /* An error occurred. */ + if (response_needed) + { + write_enn (own_buf); + putpkt (own_buf); + } + } + disable_async_io (); - while (1) + while (!exit_requested) { unsigned char sig; int packet_len; int new_packet_len = -1; + response_needed = 0; packet_len = getpkt (own_buf); if (packet_len <= 0) break; + response_needed = 1; i = 0; ch = own_buf[i++]; @@ -978,39 +1194,38 @@ main (int argc, char *argv[]) handle_general_set (own_buf); break; case 'D': + require_running (own_buf); fprintf (stderr, "Detaching from inferior\n"); if (detach_inferior () != 0) - { - write_enn (own_buf); - putpkt (own_buf); - } + write_enn (own_buf); else { write_ok (own_buf); - putpkt (own_buf); - remote_close (); - /* If we are attached, then we can exit. Otherwise, we - need to hang around doing nothing, until the child - is gone. */ - if (!attached) - join_inferior (); + if (extended_protocol) + { + /* Treat this like a normal program exit. */ + signal = 0; + status = 'W'; + } + else + { + putpkt (own_buf); + remote_close (); - exit (0); + /* If we are attached, then we can exit. Otherwise, we + need to hang around doing nothing, until the child + is gone. */ + if (!attached) + join_inferior (); + + exit (0); + } } + break; case '!': - if (attached == 0) - { - extended_protocol = 1; - prepare_resume_reply (own_buf, status, signal); - } - else - { - /* We can not use the extended protocol if we are - attached, because we can not restart the running - program. So return unrecognized. */ - own_buf[0] = '\0'; - } + extended_protocol = 1; + write_ok (own_buf); break; case '?': prepare_resume_reply (own_buf, status, signal); @@ -1020,12 +1235,18 @@ main (int argc, char *argv[]) { unsigned long gdb_id, thread_id; + require_running (own_buf); gdb_id = strtoul (&own_buf[2], NULL, 16); - thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) + if (gdb_id == 0 || gdb_id == -1) + thread_id = gdb_id; + else { - write_enn (own_buf); - break; + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) + { + write_enn (own_buf); + break; + } } if (own_buf[1] == 'g') @@ -1048,15 +1269,18 @@ main (int argc, char *argv[]) } break; case 'g': + require_running (own_buf); set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': + require_running (own_buf); set_desired_inferior (1); registers_from_string (&own_buf[1]); write_ok (own_buf); break; case 'm': + require_running (own_buf); decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_memory (mem_addr, mem_buf, len) == 0) convert_int_to_ascii (mem_buf, own_buf, len); @@ -1064,6 +1288,7 @@ main (int argc, char *argv[]) write_enn (own_buf); break; case 'M': + require_running (own_buf); decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); @@ -1071,6 +1296,7 @@ main (int argc, char *argv[]) write_enn (own_buf); break; case 'X': + require_running (own_buf); if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 || write_inferior_memory (mem_addr, mem_buf, len) != 0) @@ -1079,6 +1305,7 @@ main (int argc, char *argv[]) write_ok (own_buf); break; case 'C': + require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); @@ -1087,6 +1314,7 @@ main (int argc, char *argv[]) myresume (own_buf, 0, &signal, &status); break; case 'S': + require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); @@ -1095,10 +1323,12 @@ main (int argc, char *argv[]) myresume (own_buf, 1, &signal, &status); break; case 'c': + require_running (own_buf); signal = 0; myresume (own_buf, 0, &signal, &status); break; case 's': + require_running (own_buf); signal = 0; myresume (own_buf, 1, &signal, &status); break; @@ -1121,6 +1351,7 @@ main (int argc, char *argv[]) { int res; + require_running (own_buf); res = (*the_target->insert_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); @@ -1151,6 +1382,7 @@ main (int argc, char *argv[]) { int res; + require_running (own_buf); res = (*the_target->remove_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); @@ -1163,20 +1395,24 @@ main (int argc, char *argv[]) break; } case 'k': + response_needed = 0; + if (!target_running ()) + /* The packet we received doesn't make sense - but we + can't reply to it, either. */ + goto restart; + fprintf (stderr, "Killing inferior\n"); kill_inferior (); - /* When using the extended protocol, we start up a new - debugging session. The traditional protocol will - exit instead. */ + + /* When using the extended protocol, we wait with no + program running. The traditional protocol will exit + instead. */ if (extended_protocol) { - write_ok (own_buf); - fprintf (stderr, "GDBserver restarting\n"); - - /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); + status = 'X'; + signal = TARGET_SIGNAL_KILL; + was_running = 0; goto restart; - break; } else { @@ -1187,6 +1423,7 @@ main (int argc, char *argv[]) { unsigned long gdb_id, thread_id; + require_running (own_buf); gdb_id = strtoul (&own_buf[1], NULL, 16); thread_id = gdb_id_to_thread_id (gdb_id); if (thread_id == 0) @@ -1202,18 +1439,25 @@ main (int argc, char *argv[]) } break; case 'R': + response_needed = 0; + /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { - kill_inferior (); - write_ok (own_buf); + if (target_running ()) + kill_inferior (); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); + if (program_argv != NULL) + signal = start_inferior (program_argv, &status); + else + { + status = 'X'; + signal = TARGET_SIGNAL_KILL; + } goto restart; - break; } else { @@ -1242,45 +1486,45 @@ main (int argc, char *argv[]) else putpkt (own_buf); - if (status == 'W') - fprintf (stderr, - "\nChild exited with status %d\n", signal); - if (status == 'X') - fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", - target_signal_to_host (signal), - target_signal_to_name (signal)); - if (status == 'W' || status == 'X') + response_needed = 0; + + if (was_running && (status == 'W' || status == 'X')) { - if (extended_protocol) - { - fprintf (stderr, "Killing inferior\n"); - kill_inferior (); - write_ok (own_buf); - fprintf (stderr, "GDBserver restarting\n"); + was_running = 0; - /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); - goto restart; - break; - } + if (status == 'W') + fprintf (stderr, + "\nChild exited with status %d\n", signal); + if (status == 'X') + fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", + target_signal_to_host (signal), + target_signal_to_name (signal)); + + if (extended_protocol) + goto restart; else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } - } - /* We come here when getpkt fails. + if (status != 'W' && status != 'X') + was_running = 1; + } - For the extended remote protocol we exit (and this is the only - way we gracefully exit!). + /* If an exit was requested (using the "monitor exit" command), + terminate now. The only other way to get here is for + getpkt to fail; close the connection and reopen it at the + top of the loop. */ - For the traditional remote protocol close the connection, - and re-open it at the top of the loop. */ - if (extended_protocol) + if (exit_requested) { remote_close (); + if (attached && target_running ()) + detach_inferior (); + else if (target_running ()) + kill_inferior (); exit (0); } else Index: src/gdb/remote.c =================================================================== --- src.orig/gdb/remote.c 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/remote.c 2007-12-07 15:05:13.000000000 -0500 @@ -237,6 +237,15 @@ struct remote_state a buffer in the stub), this will be set to that packet size. Otherwise zero, meaning to use the guessed size. */ long explicit_packet_size; + + /* remote_wait is normally called when the target is running and + waits for a stop reply packet. But sometimes we need to call it + when the target is already stopped. We can send a "?" packet + and have remote_wait read the response. Or, if we already have + the response, we can stash it in BUF and tell remote_wait to + skip calling getpkt. This flag is set when BUF contains a + stop reply packet and the target is not waiting. */ + int cached_wait_status; }; /* This data could be associated with a target, but we do not always @@ -514,6 +523,10 @@ static int remote_address_size; static int remote_async_terminal_ours_p; +/* The executable file to use for "run" on the remote side. */ + +static char *remote_exec_file = ""; + \f /* User configurable variables for the number of characters in a memory read/write packet. MIN (rsa->remote_packet_size, @@ -920,6 +933,8 @@ enum { PACKET_qGetTLSAddr, PACKET_qSupported, PACKET_QPassSignals, + PACKET_vAttach, + PACKET_vRun, PACKET_MAX }; @@ -1998,11 +2013,6 @@ extended_remote_restart (void) putpkt (rs->buf); remote_fileio_reset (); - - /* Now query for status so this looks just like we restarted - gdbserver from scratch. */ - putpkt ("?"); - getpkt (&rs->buf, &rs->buf_size, 0); } \f /* Clean up connection to a remote debugger. */ @@ -2164,27 +2174,79 @@ get_offsets (void) /* Stub for catch_exception. */ +struct start_remote_args +{ + int from_tty; + + /* The current target. */ + struct target_ops *target; + + /* Non-zero if this is an extended-remote target. */ + int extended_p; +}; + static void -remote_start_remote (struct ui_out *uiout, void *from_tty_p) +remote_start_remote (struct ui_out *uiout, void *opaque) { - int from_tty = * (int *) from_tty_p; + struct remote_state *rs = get_remote_state (); + struct start_remote_args *args = opaque; + char *wait_status = NULL; immediate_quit++; /* Allow user to interrupt it. */ /* Ack any packet which the remote side has already sent. */ serial_write (remote_desc, "+", 1); + /* Check whether the target is running now. */ + putpkt ("?"); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'W' || rs->buf[0] == 'X') + { + if (args->extended_p) + { + /* We're connected, but not running. Drop out before we + call start_remote. */ + target_mark_exited (args->target); + return; + } + else + error (_("The target is not running (try extended-remote?)")); + } + else + { + if (args->extended_p) + target_mark_running (args->target); + + /* Save the reply for later. */ + wait_status = alloca (strlen (rs->buf) + 1); + strcpy (wait_status, rs->buf); + } + /* Let the stub know that we want it to return the thread. */ set_thread (-1, 0); + /* Without this, some commands which require an active target + (such as kill) won't work. This variable serves (at least) + double duty as both the pid of the target process (if it has + such), and as a flag indicating that a target is active. + These functions should be split out into seperate variables, + especially since GDB will someday have a notion of debugging + several processes. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + + /* Now, if we have thread information, update inferior_ptid. */ inferior_ptid = remote_current_thread (inferior_ptid); get_offsets (); /* Get text, data & bss offsets. */ - putpkt ("?"); /* Initiate a query from remote machine. */ - immediate_quit--; + /* Use the previously fetched status. */ + gdb_assert (wait_status != NULL); + strcpy (rs->buf, wait_status); + rs->cached_wait_status = 1; - start_remote (from_tty); /* Initialize gdb process mechanisms. */ + immediate_quit--; + start_remote (args->from_tty); /* Initialize gdb process mechanisms. */ } /* Open a connection to a remote debugger. @@ -2545,10 +2607,31 @@ remote_open_1 (char *name, int from_tty, if (!async_p) wait_forever_enabled_p = 1; + /* If we're connected to a running target, target_preopen will kill it. + But if we're connected to a target system with no running process, + then we will still be connected when it returns. Ask this question + first, before target_preopen has a chance to kill anything. */ + if (remote_desc != NULL && !target_has_execution) + { + if (!from_tty + || query (_("Already connected to a remote target. Disconnect? "))) + pop_target (); + else + error (_("Still connected.")); + } + target_preopen (from_tty); unpush_target (target); + /* This time without a query. If we were connected to an + extended-remote target and target_preopen killed the running + process, we may still be connected. If we are starting "target + remote" now, the extended-remote target will not have been + removed by unpush_target. */ + if (remote_desc != NULL && !target_has_execution) + pop_target (); + /* Make sure we send the passed signals list the next time we resume. */ xfree (last_pass_packet); last_pass_packet = NULL; @@ -2589,6 +2672,9 @@ remote_open_1 (char *name, int from_tty, } push_target (target); /* Switch to using remote target now. */ + /* Assume that the target is running, unless we learn otherwise. */ + target_mark_running (target); + /* Reset the target state; these things will be queried either by remote_query_supported or as they are needed. */ init_all_packet_configs (); @@ -2610,15 +2696,6 @@ remote_open_1 (char *name, int from_tty, this before anything involving memory or registers. */ target_find_description (); - /* Without this, some commands which require an active target (such - as kill) won't work. This variable serves (at least) double duty - as both the pid of the target process (if it has such), and as a - flag indicating that a target is active. These functions should - be split out into seperate variables, especially since GDB will - someday have a notion of debugging several processes. */ - - inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); - if (async_p) { /* With this target we start out by owning the terminal. */ @@ -2653,9 +2730,14 @@ remote_open_1 (char *name, int from_tty, all the ``target ....'' commands to share a common callback function. See cli-dump.c. */ { - struct gdb_exception ex - = catch_exception (uiout, remote_start_remote, &from_tty, - RETURN_MASK_ALL); + struct gdb_exception ex; + struct start_remote_args args; + + args.from_tty = from_tty; + args.target = target; + args.extended_p = extended_p; + + ex = catch_exception (uiout, remote_start_remote, &args, RETURN_MASK_ALL); if (ex.reason < 0) { pop_target (); @@ -2675,8 +2757,12 @@ remote_open_1 (char *name, int from_tty, getpkt (&rs->buf, &rs->buf_size, 0); } - if (exec_bfd) /* No use without an exec file. */ - remote_check_symbols (symfile_objfile); + /* If we connected to a live target, do some additional setup. */ + if (target_has_execution) + { + if (exec_bfd) /* No use without an exec file. */ + remote_check_symbols (symfile_objfile); + } } /* This takes a program previously attached to and detaches it. After @@ -2685,13 +2771,16 @@ remote_open_1 (char *name, int from_tty, die when it hits one. */ static void -remote_detach (char *args, int from_tty) +remote_detach_1 (char *args, int from_tty, int extended) { struct remote_state *rs = get_remote_state (); if (args) error (_("Argument given to \"detach\" when remotely debugging.")); + if (!target_has_execution) + error (_("No process to detach from.")); + /* Tell the remote target to detach. */ strcpy (rs->buf, "D"); putpkt (rs->buf); @@ -2706,7 +2795,24 @@ remote_detach (char *args, int from_tty) target_mourn_inferior (); if (from_tty) - puts_filtered ("Ending remote debugging.\n"); + { + if (extended) + puts_filtered ("Detached from remote process.\n"); + else + puts_filtered ("Ending remote debugging.\n"); + } +} + +static void +remote_detach (char *args, int from_tty) +{ + remote_detach_1 (args, from_tty, 0); +} + +static void +extended_remote_detach (char *args, int from_tty) +{ + remote_detach_1 (args, from_tty, 1); } /* Same as remote_detach, but don't send the "D" packet; just disconnect. */ @@ -2715,17 +2821,78 @@ static void remote_disconnect (struct target_ops *target, char *args, int from_tty) { if (args) - error (_("Argument given to \"detach\" when remotely debugging.")); + error (_("Argument given to \"disconnect\" when remotely debugging.")); /* Unregister the file descriptor from the event loop. */ if (target_is_async_p ()) serial_async (remote_desc, NULL, 0); - target_mourn_inferior (); + /* Make sure we unpush even the extended remote targets; mourn + won't do it. So call remote_mourn_1 directly instead of + target_mourn_inferior. */ + remote_mourn_1 (target); + if (from_tty) puts_filtered ("Ending remote debugging.\n"); } +/* Attach to the process specified by ARGS. If FROM_TTY is non-zero, + be chatty about it. */ + +static void +extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty) +{ + struct remote_state *rs = get_remote_state (); + pid_t pid; + char *dummy; + + if (!args) + error_no_arg (_("process-id to attach")); + + dummy = args; + pid = strtol (args, &dummy, 0); + /* Some targets don't set errno on errors, grrr! */ + if (pid == 0 && args == dummy) + error (_("Illegal process-id: %s."), args); + + if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE) + error (_("This target does not support attaching to a process")); + + sprintf (rs->buf, "vAttach;%x", pid); + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vAttach]) == PACKET_OK) + { + if (from_tty) + printf_unfiltered (_("Attached to %s\n"), + target_pid_to_str (pid_to_ptid (pid))); + + /* We have a wait response; reuse it. */ + rs->cached_wait_status = 1; + } + else if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE) + error (_("This target does not support attaching to a process")); + else + error (_("Attaching to %s failed"), + target_pid_to_str (pid_to_ptid (pid))); + + target_mark_running (target); + inferior_ptid = pid_to_ptid (pid); +} + +static void +extended_remote_attach (char *args, int from_tty) +{ + extended_remote_attach_1 (&extended_remote_ops, args, from_tty); +} + +static void +extended_async_remote_attach (char *args, int from_tty) +{ + extended_remote_attach_1 (&extended_async_remote_ops, args, from_tty); +} + /* Convert hex digit A to a number. */ static int @@ -2850,7 +3017,7 @@ remote_vcont_resume (ptid_t ptid, int st { struct remote_state *rs = get_remote_state (); int pid = PIDGET (ptid); - char *buf = NULL, *outbuf; + char *outbuf; struct cleanup *old_cleanup; if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN) @@ -3208,9 +3375,15 @@ remote_wait (ptid_t ptid, struct target_ { char *buf, *p; - ofunc = signal (SIGINT, remote_interrupt); - getpkt (&rs->buf, &rs->buf_size, 1); - signal (SIGINT, ofunc); + if (rs->cached_wait_status) + /* Use the cached wait status, but only once. */ + rs->cached_wait_status = 0; + else + { + ofunc = signal (SIGINT, remote_interrupt); + getpkt (&rs->buf, &rs->buf_size, 1); + signal (SIGINT, ofunc); + } buf = rs->buf; @@ -3417,15 +3590,21 @@ remote_async_wait (ptid_t ptid, struct t { char *buf, *p; - if (!target_is_async_p ()) - ofunc = signal (SIGINT, remote_interrupt); - /* FIXME: cagney/1999-09-27: If we're in async mode we should - _never_ wait for ever -> test on target_is_async_p(). - However, before we do that we need to ensure that the caller - knows how to take the target into/out of async mode. */ - getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p); - if (!target_is_async_p ()) - signal (SIGINT, ofunc); + if (rs->cached_wait_status) + /* Use the cached wait status, but only once. */ + rs->cached_wait_status = 0; + else + { + if (!target_is_async_p ()) + ofunc = signal (SIGINT, remote_interrupt); + /* FIXME: cagney/1999-09-27: If we're in async mode we should + _never_ wait for ever -> test on target_is_async_p(). + However, before we do that we need to ensure that the caller + knows how to take the target into/out of async mode. */ + getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p); + if (!target_is_async_p ()) + signal (SIGINT, ofunc); + } buf = rs->buf; @@ -4694,6 +4873,7 @@ putpkt (char *buf) static int putpkt_binary (char *buf, int cnt) { + struct remote_state *rs = get_remote_state (); int i; unsigned char csum = 0; char *buf2 = alloca (cnt + 6); @@ -4702,6 +4882,10 @@ putpkt_binary (char *buf, int cnt) int tcount = 0; char *p; + /* We're sending out a new packet. Make sure we don't look at a + stale cached response. */ + rs->cached_wait_status = 0; + /* Copy the packet into buffer BUF2, encapsulating it and giving it a checksum. */ @@ -5003,11 +5187,16 @@ getpkt (char **buf, static int getpkt_sane (char **buf, long *sizeof_buf, int forever) { + struct remote_state *rs = get_remote_state (); int c; int tries; int timeout; int val; + /* We're reading a new response. Make sure we don't look at a + previously cached response. */ + rs->cached_wait_status = 0; + strcpy (*buf, "timeout"); if (forever) @@ -5139,19 +5328,6 @@ remote_async_mourn (void) remote_mourn_1 (&remote_async_ops); } -static void -extended_remote_mourn (void) -{ - /* We do _not_ want to mourn the target like this; this will - remove the extended remote target from the target stack, - and the next time the user says "run" it'll fail. - - FIXME: What is the right thing to do here? */ -#if 0 - remote_mourn_1 (&extended_remote_ops); -#endif -} - /* Worker function for remote_mourn. */ static void remote_mourn_1 (struct target_ops *target) @@ -5160,71 +5336,167 @@ remote_mourn_1 (struct target_ops *targe generic_mourn_inferior (); } -/* In the extended protocol we want to be able to do things like - "run" and have them basically work as expected. So we need - a special create_inferior function. +static void +extended_remote_mourn_1 (struct target_ops *target) +{ + struct remote_state *rs = get_remote_state (); + + /* Unlike "target remote", we do not want to unpush the target; then + the next time the user says "run", we won't be connected. */ + + /* Call common code to mark the inferior as not running. */ + generic_mourn_inferior (); + + /* Check whether the target is running now - some remote stubs + automatically restart after kill. */ + putpkt ("?"); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'S' || rs->buf[0] == 'T') + { + /* Assume that the target has been restarted. Set inferior_ptid + so that bits of core GDB realizes there's something here, e.g., + so that the user can say "kill" again. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + } + else + { + /* Mark this (still pushed) target as not executable until we + restart it. */ + target_mark_exited (target); + } +} - FIXME: One day add support for changing the exec file - we're debugging, arguments and an environment. */ +static void +extended_remote_mourn (void) +{ + extended_remote_mourn_1 (&extended_remote_ops); +} static void -extended_remote_create_inferior (char *exec_file, char *args, - char **env, int from_tty) +extended_async_remote_mourn (void) +{ + extended_remote_mourn_1 (&extended_async_remote_ops); +} + +static int +extended_remote_run (char *args) { - /* Rip out the breakpoints; we'll reinsert them after restarting - the remote server. */ - remove_breakpoints (); + struct remote_state *rs = get_remote_state (); + char *p; + int len; - /* Now restart the remote server. */ - extended_remote_restart (); + /* If the user has disabled vRun support, or we have detected that + support is not available, do not try it. */ + if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE) + return -1; - /* NOTE: We don't need to recheck for a target description here; but - if we gain the ability to switch the remote executable we may - need to, if for instance we are running a process which requested - different emulated hardware from the operating system. A - concrete example of this is ARM GNU/Linux, where some binaries - will have a legacy FPA coprocessor emulated and others may have - access to a hardware VFP unit. */ - - /* Now put the breakpoints back in. This way we're safe if the - restart function works via a unix fork on the remote side. */ - insert_breakpoints (); + strcpy (rs->buf, "vRun;"); + len = strlen (rs->buf); - /* Clean up from the last time we were running. */ - clear_proceed_status (); + if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ()) + error (_("Remote file name too long for run packet")); + len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf + len, 0); + + if (*args) + { + struct cleanup *back_to; + int i; + char **argv; + + argv = buildargv (args); + back_to = make_cleanup ((void (*) (void *)) freeargv, argv); + for (i = 0; argv[i] != NULL; i++) + { + if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ()) + error (_("Argument list too long for run packet")); + rs->buf[len++] = ';'; + len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf + len, 0); + } + do_cleanups (back_to); + } + + rs->buf[len++] = '\0'; + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vRun]) == PACKET_OK) + { + /* We have a wait response; we don't need it, though. All is well. */ + return 0; + } + else if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE) + /* It wasn't disabled before, but it is now. */ + return -1; + else + { + if (remote_exec_file == NULL) + error (_("Running the default executable on the remote target failed; " + "try \"set remote exec-file\"?")); + else + error (_("Running \"%s\" on the remote target failed"), + remote_exec_file); + } } -/* Async version of extended_remote_create_inferior. */ +/* In the extended protocol we want to be able to do things like + "run" and have them basically work as expected. So we need + a special create_inferior function. We support changing the + executable file and the command line arguments, but not the + environment. */ + static void -extended_remote_async_create_inferior (char *exec_file, char *args, - char **env, int from_tty) +extended_remote_create_inferior_1 (char *exec_file, char *args, + char **env, int from_tty, + int async_p) { - /* Rip out the breakpoints; we'll reinsert them after restarting - the remote server. */ - remove_breakpoints (); - /* If running asynchronously, register the target file descriptor with the event loop. */ - if (target_can_async_p ()) + if (async_p && target_can_async_p ()) target_async (inferior_event_handler, 0); /* Now restart the remote server. */ - extended_remote_restart (); + if (extended_remote_run (args) == -1) + { + /* vRun was not supported. Fail if we need it to do what the + user requested. */ + if (remote_exec_file[0]) + error (_("Remote target does not support \"set remote exec-file\"")); + if (args[0]) + error (_("Remote target does not support \"set args\" or run <ARGS>")); + + /* Fall back to "R". */ + extended_remote_restart (); + } + + /* Now mark the inferior as running before we do anything else. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + if (async_p) + target_mark_running (&extended_async_remote_ops); + else + target_mark_running (&extended_remote_ops); - /* NOTE: We don't need to recheck for a target description here; but - if we gain the ability to switch the remote executable we may - need to, if for instance we are running a process which requested - different emulated hardware from the operating system. A - concrete example of this is ARM GNU/Linux, where some binaries - will have a legacy FPA coprocessor emulated and others may have - access to a hardware VFP unit. */ - - /* Now put the breakpoints back in. This way we're safe if the - restart function works via a unix fork on the remote side. */ - insert_breakpoints (); + /* Get updated offsets, if the stub uses qOffsets. */ + get_offsets (); /* Clean up from the last time we were running. */ - clear_proceed_status (); + init_thread_list (); + init_wait_for_inferior (); +} + +static void +extended_remote_create_inferior (char *exec_file, char *args, + char **env, int from_tty) +{ + extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0); +} + +static void +extended_remote_async_create_inferior (char *exec_file, char *args, + char **env, int from_tty) +{ + extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1); } \f @@ -5782,6 +6054,12 @@ remote_xfer_partial (struct target_ops * int xfered; errno = 0; + /* If the remote target is connected but not running, we should + pass this request down to a lower stratum (e.g. the executable + file). */ + if (!target_has_execution) + return 0; + if (writebuf != NULL) xfered = remote_write_bytes (offset, writebuf, len); else @@ -6982,6 +7260,8 @@ Specify the serial device it is connecte extended_remote_ops.to_open = extended_remote_open; extended_remote_ops.to_create_inferior = extended_remote_create_inferior; extended_remote_ops.to_mourn_inferior = extended_remote_mourn; + extended_remote_ops.to_detach = extended_remote_detach; + extended_remote_ops.to_attach = extended_remote_attach; } static int @@ -7114,7 +7394,9 @@ init_extended_async_remote_ops (void) Specify the serial device it is connected to (e.g. /dev/ttya).", extended_async_remote_ops.to_open = extended_remote_async_open; extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior; - extended_async_remote_ops.to_mourn_inferior = extended_remote_mourn; + extended_async_remote_ops.to_mourn_inferior = extended_async_remote_mourn; + extended_async_remote_ops.to_detach = extended_remote_detach; + extended_async_remote_ops.to_attach = extended_async_remote_attach; } static void @@ -7369,6 +7651,12 @@ Show the maximum size of the address (in add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink], "vFile:unlink", "hostio-unlink", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_vAttach], + "vAttach", "attach", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vRun], + "vRun", "run", 0); + /* Keep the old ``set remote Z-packet ...'' working. Each individual Z sub-packet has its own set and show commands, but users may have sets to this variable in their .gdbinit files (or in their @@ -7401,6 +7689,13 @@ Transfer files to and from the remote ta _("Delete a remote file."), &remote_cmdlist); + remote_exec_file = xstrdup (""); + add_setshow_string_noescape_cmd ("exec-file", class_files, + &remote_exec_file, _("\ +Set the remote pathname for \"run\""), _("\ +Show the remote pathname for \"run\""), NULL, NULL, NULL, + &remote_set_cmdlist, &remote_show_cmdlist); + /* Eventually initialize fileio. See fileio.c */ initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist); } Index: src/gdb/testsuite/gdb.server/ext-attach.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/testsuite/gdb.server/ext-attach.c 2007-12-07 10:38:30.000000000 -0500 @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2007 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/>. */ + +/* This program is intended to be started outside of gdb, and then + attached to by gdb. It loops for a while, but not forever. */ + +#include <unistd.h> + +int main () +{ + int i; + + for (i = 0; i < 120; i++) + sleep (1); + + return 0; +} Index: src/gdb/testsuite/gdb.server/ext-attach.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/testsuite/gdb.server/ext-attach.exp 2007-12-07 10:38:30.000000000 -0500 @@ -0,0 +1,77 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2007 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/>. + +# Test attaching to already-running programs using extended-remote. + +load_lib gdbserver-support.exp + +set testfile "ext-attach" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [skip_gdbserver_tests] } { + return 0 +} + +# On SPU, this test currently fails because "sleep" is not supported. +if { [istarget "spu*-*-*"] } { + return 0 +} + +# We need to use TCL's exec to get the pid. +if [is_remote target] then { + return 0 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested ext-attach.exp + return -1 +} + +gdb_exit +gdb_start +gdb_load $binfile +gdb_reinitialize_dir $srcdir/$subdir + +set target_exec [gdbserver_download] +gdbserver_start_extended + +gdb_test "set remote exec-file $target_exec" "" "set remote exec-file" + +# Start the program running and then wait for a bit, to be sure +# that it can be attached to. +set testpid [eval exec $binfile &] +exec sleep 2 +if { [istarget "*-*-cygwin*"] } { + # testpid is the Cygwin PID, GDB uses the Windows PID, which might be + # different due to the way fork/exec works. + set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] +} + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 1" +gdb_test "backtrace" ".*main.*" "backtrace 1" + +gdb_test "detach" "Detached from remote process\\." +gdb_test "backtrace" "No stack\\." "backtrace with no program" + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 2" +gdb_test "backtrace" ".*main.*" "backtrace 2" + +gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y" +gdb_test "monitor exit" "" Index: src/gdb/testsuite/gdb.server/ext-run.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ src/gdb/testsuite/gdb.server/ext-run.exp 2007-12-07 10:38:30.000000000 -0500 @@ -0,0 +1,48 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2007 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/>. + +# Test running programs using extended-remote. + +load_lib gdbserver-support.exp + +set testfile "server" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [skip_gdbserver_tests] } { + return 0 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_load $binfile +gdb_reinitialize_dir $srcdir/$subdir + +set target_exec [gdbserver_download] +gdbserver_start_extended + +gdb_test "set remote exec-file $target_exec" "" "set remote exec-file" + +gdb_breakpoint main +gdb_test "run" "Breakpoint.* main .*" "continue to main" + +gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y" +gdb_test "monitor exit" "" Index: src/gdb/testsuite/lib/gdbserver-support.exp =================================================================== --- src.orig/gdb/testsuite/lib/gdbserver-support.exp 2007-12-07 10:37:55.000000000 -0500 +++ src/gdb/testsuite/lib/gdbserver-support.exp 2007-12-07 10:38:30.000000000 -0500 @@ -129,13 +129,10 @@ proc skip_gdbserver_tests { } { return 0 } -# Start a gdbserver process running SERVER_EXEC, and connect GDB -# to it. CHILD_ARGS are passed to the inferior. -# -# Returns the target protocol and socket to connect to. +# Download the currently loaded program to the target if necessary. +# Return the target system filename. -proc gdbserver_spawn { child_args } { - global portnum +proc gdbserver_download { } { global gdbserver_host_exec global gdbserver_host_mtime global gdbserver_server_exec @@ -169,6 +166,17 @@ proc gdbserver_spawn { child_args } { } } + return $gdbserver_server_exec +} + +# Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS. +# The port will be filled in between them automatically. +# +# Returns the target protocol and socket to connect to. + +proc gdbserver_start { options arguments } { + global portnum + # Port id -- either specified in baseboard file, or managed here. if [target_info exists gdb,socketport] { set portnum [target_info gdb,socketport] @@ -179,7 +187,7 @@ proc gdbserver_spawn { child_args } { # Extract the local and remote host ids from the target board struct. if [target_info exists sockethost] { - set debughost [target_info sockethost] + set debughost [target_info sockethost] } else { set debughost "localhost:" } @@ -196,23 +204,23 @@ proc gdbserver_spawn { child_args } { # Export the host:port pair. set gdbport $debughost$portnum - # Fire off the debug agent. This flavour of gdbserver takes as - # arguments the port information, the name of the executable file to - # be debugged, and any arguments. - set gdbserver_command "$gdbserver :$portnum $gdbserver_server_exec" - if { $child_args != "" } { - append gdbserver_command " $child_args" + # Fire off the debug agent. + set gdbserver_command "$gdbserver" + if { $options != "" } { + append gdbserver_command " $options" + } + append gdbserver_command " :$portnum" + if { $arguments != "" } { + append gdbserver_command " $arguments" } set server_spawn_id [remote_spawn target $gdbserver_command] - # Wait for the server to produce at least one line and an additional - # character of output. This will wait until any TCP socket has been - # created, so that GDB can connect. + # Wait for the server to open its TCP socket, so that GDB can connect. expect { -i $server_spawn_id -notransfer - -re ".*\n." { } + -re "Listening on" { } } # We can't just call close, because if gdbserver is local then that means @@ -231,6 +239,24 @@ proc gdbserver_spawn { child_args } { return [list $protocol $gdbport] } +# Start a gdbserver process running SERVER_EXEC, and connect GDB +# to it. CHILD_ARGS are passed to the inferior. +# +# Returns the target protocol and socket to connect to. + +proc gdbserver_spawn { child_args } { + set target_exec [gdbserver_download] + + # Fire off the debug agent. This flavour of gdbserver takes as + # arguments the port information, the name of the executable file to + # be debugged, and any arguments. + set arguments "$target_exec" + if { $child_args != "" } { + append arguments " $child_args" + } + return [gdbserver_start "" $arguments] +} + # Start a gdbserver process running HOST_EXEC and pass CHILD_ARGS # to it. Return 0 on success, or non-zero on failure. @@ -268,3 +294,12 @@ proc gdbserver_reconnect { } { return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] } + +# Start and connect to a gdbserver in extended mode. +proc gdbserver_start_extended { } { + set res [gdbserver_start "--multi" ""] + set gdbserver_protocol "extended-[lindex $res 0]" + set gdbserver_gdbport [lindex $res 1] + + return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] +} Index: src/gdb/NEWS =================================================================== --- src.orig/gdb/NEWS 2007-12-07 16:09:57.000000000 -0500 +++ src/gdb/NEWS 2007-12-07 16:13:30.000000000 -0500 @@ -35,6 +35,9 @@ targets even when the libthread_db libra * The GDB remote stub, gdbserver, now supports the new file transfer commands (remote put, remote get, and remote delete). +* The GDB remote stub, gdbserver, now supports run and attach in +extended-remote mode. + * hppa*64*-*-hpux11* target broken The debugger is unable to start a program and fails with the following error: "Error trying to get information about dynamic linker". @@ -67,6 +70,13 @@ vFile:pwrite: vFile:unlink: Open, close, read, write, and delete files on the remote system. +vAttach + Attach to an existing process on the remote system, in extended-remote + mode. + +vRun + Run a new process on the remote system, in extended-remote mode. + *** Changes in GDB 6.7 * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb, ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2007-12-10 15:34 ` Daniel Jacobowitz @ 2008-01-24 1:50 ` Pedro Alves 2008-01-30 0:55 ` Daniel Jacobowitz 0 siblings, 1 reply; 11+ messages in thread From: Pedro Alves @ 2008-01-24 1:50 UTC (permalink / raw) To: gdb-patches Hi Daniel, I'm finally looking at this very, very useful patch. I've got a few small comments. + if (remote_exec_file == NULL) + error (_("Running the default executable on the remote target failed; " It seems remote_exec_file can never be NULL. Certainly you wanted to check for empty string. In gdbserver/linux-low.c, when you do this, free (all_processes.head); all_processes.head = all_processes.tail = NULL; Isn't this leaking every ]all_processes.head, all_processes.tail] if (all_processes.head != all_processes.tail) ? shouldn't you be doing something like: for_each_inferiors (&all_processes, free_process); all_processes.head = all_processes.tail = NULL; ? In addition to that, this almost works on Windows targets, we just need to clean up the state when the inferiors are detached or killed, like you're doing on linux. If you prefer, I can post a patch once this goes in. -- Pedro Alves ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2008-01-24 1:50 ` Pedro Alves @ 2008-01-30 0:55 ` Daniel Jacobowitz 2008-01-30 5:55 ` Eli Zaretskii 2008-02-14 21:09 ` --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) Pedro Alves 0 siblings, 2 replies; 11+ messages in thread From: Daniel Jacobowitz @ 2008-01-30 0:55 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches On Thu, Jan 24, 2008 at 01:50:01AM +0000, Pedro Alves wrote: > Hi Daniel, > > I'm finally looking at this very, very useful patch. > > I've got a few small comments. > > + if (remote_exec_file == NULL) > + error (_("Running the default executable on the remote target failed; " > > It seems remote_exec_file can never be NULL. Certainly you wanted to > check for empty string. Right you are. > In gdbserver/linux-low.c, when you do this, > > free (all_processes.head); > all_processes.head = all_processes.tail = NULL; > > Isn't this leaking every ]all_processes.head, all_processes.tail] > if (all_processes.head != all_processes.tail) ? > > shouldn't you be doing something like: > > for_each_inferiors (&all_processes, free_process); > all_processes.head = all_processes.tail = NULL; > > ? There should never be more than one thing on the list at this point. We've detached from or killed every thread; there's one left on the list for bookkeeping reasons (to avoid some NULL pointers, I believe), but the inferior's all gone now. > In addition to that, this almost works on Windows targets, we just > need to clean up the state when the inferiors are detached or > killed, like you're doing on linux. If you prefer, I can post a > patch once this goes in. Great! Yes, please do. Here's what I retested and checked in. -- Daniel Jacobowitz CodeSourcery 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com> * linux-low.c (linux_attach_lwp): Do not _exit after errors. (linux_kill, linux_detach): Clean up the process list. * remote-utils.c (remote_open): Improve port number parsing. (putpkt_binary, input_interrupt): Only send interrupts if the target is running. * server.c (extended_protocol): Make static. (attached): Define earlier. (exit_requested, response_needed, program_argv): New variables. (target_running): New. (start_inferior): Clear attached here. (attach_inferior): Set attached here. (require_running): Define. (handle_query): Use require_running and target_running. Implement "monitor exit". (handle_v_attach, handle_v_run): New. (handle_v_requests): Use require_running. Handle vAttach and vRun. (gdbserver_usage): Update. (main): Redo argument parsing. Handle --debug and --multi. Handle --attach along with other options or after the port. Save program_argv. Support no initial program. Resynchronize communication with GDB after an error. Handle "monitor exit". Use require_running and target_running. Always allow the extended protocol. Do not error out for Hc0 or Hc-1. Do not automatically restart in extended mode. * README: Refer to the GDB manual. Update --attach usage. 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com> * remote.c (struct remote_state): Add cached_wait_status. (remote_exec_file): New variable. (PACKET_vAttach, PACKET_vRun): New constants. (extended_remote_restart): Do not query for status. (struct start_remote_args): New. (remote_start_remote): Take it as a second argument. Check whether the target is running. Issue an error for non-running non-extended targets. Cache the wait status. Set inferior_ptid here. (remote_open_1): Prompt to disconnect non-running targets. Make sure the target is marked running. Do not set inferior_ptid here. Update call to remote_start_remote. Do not call remote_check_symbols if the target is not running. (remote_detach_1): Rename from remote_detach. Take an EXTENDED argument. Handle a non-running target. (remote_detach): Use it. (extended_remote_detach): New. (remote_disconnect): Fix typo. Use remoute_mourn_1. (extended_remote_attach_1, extended_remote_attach) (extended_async_remote_attach): New. (remote_vcont_resume): Remove unused variable. (remote_wait, remote_async_wait): Use any cached wait status. (putpkt_binary, getpkt): Clear any cached wait status. (extended_remoute_mourn_1): New. (extended_remote_mourn): Use it. (extended_async_remote_mourn, extended_remote_run): New. (extended_remote_create_inferior_1): New. (extended_remote_create_inferior): Use it. (extended_remote_async_create_inferior): Likewise. (remote_xfer_partial): Skip for non-executing targets. (init_extended_remote_ops): Set to_detach and to_attach. (init_extended_async_remote_ops): Likewise. Use extended_async_remote_mourn. (_initialize_remote): Register vAttach, vRun, and set remote exec-file. * NEWS: Mention vAttach, vRun, and gdbserver extended-remote support. 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com> * gdb.server/ext-attach.c, gdb.server/ext-attach.exp, gdb.server/ext-run.exp: New files. * lib/gdbserver-support.exp (gdbserver_download): New. (gdbserver_start): New. Update gdbserver expected output. (gdbserver_spawn): Use them. (gdbserver_start_extended): New. 2008-01-29 Daniel Jacobowitz <dan@codesourcery.com> * gdb.texinfo (Using the `gdbserver' Program): Add security warning. Rearrange into subsections and subsubsections. Document --multi and --debug. Correct --with-sysroot typo. Update --attach usage. Make load reference clearer. Document monitor exit. (Remote Configuration): Document set remote exec-file, attach-packet, and run-packet. (Packets): Document vAttach and vRun. Index: NEWS =================================================================== RCS file: /cvs/src/src/gdb/NEWS,v retrieving revision 1.253 diff -u -p -r1.253 NEWS --- NEWS 29 Jan 2008 22:47:19 -0000 1.253 +++ NEWS 30 Jan 2008 00:16:31 -0000 @@ -50,6 +50,9 @@ targets even when the libthread_db libra * The GDB remote stub, gdbserver, now supports the new file transfer commands (remote put, remote get, and remote delete). +* The GDB remote stub, gdbserver, now supports run and attach in +extended-remote mode. + * hppa*64*-*-hpux11* target broken The debugger is unable to start a program and fails with the following error: "Error trying to get information about dynamic linker". @@ -85,6 +88,13 @@ vFile:unlink: * GDB on GNU/Linux and HP/UX can now debug through "exec" of a new process. +vAttach + Attach to an existing process on the remote system, in extended-remote + mode. + +vRun + Run a new process on the remote system, in extended-remote mode. + *** Changes in GDB 6.7 * Resolved 101 resource leaks, null pointer dereferences, etc. in gdb, Index: remote.c =================================================================== RCS file: /cvs/src/src/gdb/remote.c,v retrieving revision 1.277 diff -u -p -r1.277 remote.c --- remote.c 23 Jan 2008 20:43:30 -0000 1.277 +++ remote.c 30 Jan 2008 00:16:32 -0000 @@ -237,6 +237,15 @@ struct remote_state a buffer in the stub), this will be set to that packet size. Otherwise zero, meaning to use the guessed size. */ long explicit_packet_size; + + /* remote_wait is normally called when the target is running and + waits for a stop reply packet. But sometimes we need to call it + when the target is already stopped. We can send a "?" packet + and have remote_wait read the response. Or, if we already have + the response, we can stash it in BUF and tell remote_wait to + skip calling getpkt. This flag is set when BUF contains a + stop reply packet and the target is not waiting. */ + int cached_wait_status; }; /* This data could be associated with a target, but we do not always @@ -514,6 +523,10 @@ static int remote_address_size; static int remote_async_terminal_ours_p; +/* The executable file to use for "run" on the remote side. */ + +static char *remote_exec_file = ""; + \f /* User configurable variables for the number of characters in a memory read/write packet. MIN (rsa->remote_packet_size, @@ -920,6 +933,8 @@ enum { PACKET_qGetTLSAddr, PACKET_qSupported, PACKET_QPassSignals, + PACKET_vAttach, + PACKET_vRun, PACKET_MAX }; @@ -1993,11 +2008,6 @@ extended_remote_restart (void) putpkt (rs->buf); remote_fileio_reset (); - - /* Now query for status so this looks just like we restarted - gdbserver from scratch. */ - putpkt ("?"); - getpkt (&rs->buf, &rs->buf_size, 0); } \f /* Clean up connection to a remote debugger. */ @@ -2159,27 +2169,79 @@ get_offsets (void) /* Stub for catch_exception. */ +struct start_remote_args +{ + int from_tty; + + /* The current target. */ + struct target_ops *target; + + /* Non-zero if this is an extended-remote target. */ + int extended_p; +}; + static void -remote_start_remote (struct ui_out *uiout, void *from_tty_p) +remote_start_remote (struct ui_out *uiout, void *opaque) { - int from_tty = * (int *) from_tty_p; + struct remote_state *rs = get_remote_state (); + struct start_remote_args *args = opaque; + char *wait_status = NULL; immediate_quit++; /* Allow user to interrupt it. */ /* Ack any packet which the remote side has already sent. */ serial_write (remote_desc, "+", 1); + /* Check whether the target is running now. */ + putpkt ("?"); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'W' || rs->buf[0] == 'X') + { + if (args->extended_p) + { + /* We're connected, but not running. Drop out before we + call start_remote. */ + target_mark_exited (args->target); + return; + } + else + error (_("The target is not running (try extended-remote?)")); + } + else + { + if (args->extended_p) + target_mark_running (args->target); + + /* Save the reply for later. */ + wait_status = alloca (strlen (rs->buf) + 1); + strcpy (wait_status, rs->buf); + } + /* Let the stub know that we want it to return the thread. */ set_thread (-1, 0); + /* Without this, some commands which require an active target + (such as kill) won't work. This variable serves (at least) + double duty as both the pid of the target process (if it has + such), and as a flag indicating that a target is active. + These functions should be split out into seperate variables, + especially since GDB will someday have a notion of debugging + several processes. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + + /* Now, if we have thread information, update inferior_ptid. */ inferior_ptid = remote_current_thread (inferior_ptid); get_offsets (); /* Get text, data & bss offsets. */ - putpkt ("?"); /* Initiate a query from remote machine. */ - immediate_quit--; + /* Use the previously fetched status. */ + gdb_assert (wait_status != NULL); + strcpy (rs->buf, wait_status); + rs->cached_wait_status = 1; - start_remote (from_tty); /* Initialize gdb process mechanisms. */ + immediate_quit--; + start_remote (args->from_tty); /* Initialize gdb process mechanisms. */ } /* Open a connection to a remote debugger. @@ -2540,10 +2602,31 @@ remote_open_1 (char *name, int from_tty, if (!async_p) wait_forever_enabled_p = 1; + /* If we're connected to a running target, target_preopen will kill it. + But if we're connected to a target system with no running process, + then we will still be connected when it returns. Ask this question + first, before target_preopen has a chance to kill anything. */ + if (remote_desc != NULL && !target_has_execution) + { + if (!from_tty + || query (_("Already connected to a remote target. Disconnect? "))) + pop_target (); + else + error (_("Still connected.")); + } + target_preopen (from_tty); unpush_target (target); + /* This time without a query. If we were connected to an + extended-remote target and target_preopen killed the running + process, we may still be connected. If we are starting "target + remote" now, the extended-remote target will not have been + removed by unpush_target. */ + if (remote_desc != NULL && !target_has_execution) + pop_target (); + /* Make sure we send the passed signals list the next time we resume. */ xfree (last_pass_packet); last_pass_packet = NULL; @@ -2584,6 +2667,9 @@ remote_open_1 (char *name, int from_tty, } push_target (target); /* Switch to using remote target now. */ + /* Assume that the target is running, unless we learn otherwise. */ + target_mark_running (target); + /* Reset the target state; these things will be queried either by remote_query_supported or as they are needed. */ init_all_packet_configs (); @@ -2605,15 +2691,6 @@ remote_open_1 (char *name, int from_tty, this before anything involving memory or registers. */ target_find_description (); - /* Without this, some commands which require an active target (such - as kill) won't work. This variable serves (at least) double duty - as both the pid of the target process (if it has such), and as a - flag indicating that a target is active. These functions should - be split out into seperate variables, especially since GDB will - someday have a notion of debugging several processes. */ - - inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); - if (async_p) { /* With this target we start out by owning the terminal. */ @@ -2648,9 +2725,14 @@ remote_open_1 (char *name, int from_tty, all the ``target ....'' commands to share a common callback function. See cli-dump.c. */ { - struct gdb_exception ex - = catch_exception (uiout, remote_start_remote, &from_tty, - RETURN_MASK_ALL); + struct gdb_exception ex; + struct start_remote_args args; + + args.from_tty = from_tty; + args.target = target; + args.extended_p = extended_p; + + ex = catch_exception (uiout, remote_start_remote, &args, RETURN_MASK_ALL); if (ex.reason < 0) { pop_target (); @@ -2670,8 +2752,12 @@ remote_open_1 (char *name, int from_tty, getpkt (&rs->buf, &rs->buf_size, 0); } - if (exec_bfd) /* No use without an exec file. */ - remote_check_symbols (symfile_objfile); + /* If we connected to a live target, do some additional setup. */ + if (target_has_execution) + { + if (exec_bfd) /* No use without an exec file. */ + remote_check_symbols (symfile_objfile); + } } /* This takes a program previously attached to and detaches it. After @@ -2680,13 +2766,16 @@ remote_open_1 (char *name, int from_tty, die when it hits one. */ static void -remote_detach (char *args, int from_tty) +remote_detach_1 (char *args, int from_tty, int extended) { struct remote_state *rs = get_remote_state (); if (args) error (_("Argument given to \"detach\" when remotely debugging.")); + if (!target_has_execution) + error (_("No process to detach from.")); + /* Tell the remote target to detach. */ strcpy (rs->buf, "D"); putpkt (rs->buf); @@ -2701,7 +2790,24 @@ remote_detach (char *args, int from_tty) target_mourn_inferior (); if (from_tty) - puts_filtered ("Ending remote debugging.\n"); + { + if (extended) + puts_filtered ("Detached from remote process.\n"); + else + puts_filtered ("Ending remote debugging.\n"); + } +} + +static void +remote_detach (char *args, int from_tty) +{ + remote_detach_1 (args, from_tty, 0); +} + +static void +extended_remote_detach (char *args, int from_tty) +{ + remote_detach_1 (args, from_tty, 1); } /* Same as remote_detach, but don't send the "D" packet; just disconnect. */ @@ -2710,17 +2816,78 @@ static void remote_disconnect (struct target_ops *target, char *args, int from_tty) { if (args) - error (_("Argument given to \"detach\" when remotely debugging.")); + error (_("Argument given to \"disconnect\" when remotely debugging.")); /* Unregister the file descriptor from the event loop. */ if (target_is_async_p ()) serial_async (remote_desc, NULL, 0); - target_mourn_inferior (); + /* Make sure we unpush even the extended remote targets; mourn + won't do it. So call remote_mourn_1 directly instead of + target_mourn_inferior. */ + remote_mourn_1 (target); + if (from_tty) puts_filtered ("Ending remote debugging.\n"); } +/* Attach to the process specified by ARGS. If FROM_TTY is non-zero, + be chatty about it. */ + +static void +extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty) +{ + struct remote_state *rs = get_remote_state (); + pid_t pid; + char *dummy; + + if (!args) + error_no_arg (_("process-id to attach")); + + dummy = args; + pid = strtol (args, &dummy, 0); + /* Some targets don't set errno on errors, grrr! */ + if (pid == 0 && args == dummy) + error (_("Illegal process-id: %s."), args); + + if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE) + error (_("This target does not support attaching to a process")); + + sprintf (rs->buf, "vAttach;%x", pid); + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vAttach]) == PACKET_OK) + { + if (from_tty) + printf_unfiltered (_("Attached to %s\n"), + target_pid_to_str (pid_to_ptid (pid))); + + /* We have a wait response; reuse it. */ + rs->cached_wait_status = 1; + } + else if (remote_protocol_packets[PACKET_vAttach].support == PACKET_DISABLE) + error (_("This target does not support attaching to a process")); + else + error (_("Attaching to %s failed"), + target_pid_to_str (pid_to_ptid (pid))); + + target_mark_running (target); + inferior_ptid = pid_to_ptid (pid); +} + +static void +extended_remote_attach (char *args, int from_tty) +{ + extended_remote_attach_1 (&extended_remote_ops, args, from_tty); +} + +static void +extended_async_remote_attach (char *args, int from_tty) +{ + extended_remote_attach_1 (&extended_async_remote_ops, args, from_tty); +} + /* Convert hex digit A to a number. */ static int @@ -2845,7 +3012,7 @@ remote_vcont_resume (ptid_t ptid, int st { struct remote_state *rs = get_remote_state (); int pid = PIDGET (ptid); - char *buf = NULL, *outbuf; + char *outbuf; struct cleanup *old_cleanup; if (remote_protocol_packets[PACKET_vCont].support == PACKET_SUPPORT_UNKNOWN) @@ -3203,16 +3370,22 @@ remote_wait (ptid_t ptid, struct target_ { char *buf, *p; - ofunc = signal (SIGINT, remote_interrupt); - /* If the user hit C-c before this packet, or between packets, - pretend that it was hit right here. */ - if (quit_flag) + if (rs->cached_wait_status) + /* Use the cached wait status, but only once. */ + rs->cached_wait_status = 0; + else { - quit_flag = 0; - remote_interrupt (SIGINT); + ofunc = signal (SIGINT, remote_interrupt); + /* If the user hit C-c before this packet, or between packets, + pretend that it was hit right here. */ + if (quit_flag) + { + quit_flag = 0; + remote_interrupt (SIGINT); + } + getpkt (&rs->buf, &rs->buf_size, 1); + signal (SIGINT, ofunc); } - getpkt (&rs->buf, &rs->buf_size, 1); - signal (SIGINT, ofunc); buf = rs->buf; @@ -3419,24 +3592,30 @@ remote_async_wait (ptid_t ptid, struct t { char *buf, *p; - if (!target_is_async_p ()) + if (rs->cached_wait_status) + /* Use the cached wait status, but only once. */ + rs->cached_wait_status = 0; + else { - ofunc = signal (SIGINT, remote_interrupt); - /* If the user hit C-c before this packet, or between packets, - pretend that it was hit right here. */ - if (quit_flag) + if (!target_is_async_p ()) { - quit_flag = 0; - remote_interrupt (SIGINT); + ofunc = signal (SIGINT, remote_interrupt); + /* If the user hit C-c before this packet, or between packets, + pretend that it was hit right here. */ + if (quit_flag) + { + quit_flag = 0; + remote_interrupt (SIGINT); + } } + /* FIXME: cagney/1999-09-27: If we're in async mode we should + _never_ wait for ever -> test on target_is_async_p(). + However, before we do that we need to ensure that the caller + knows how to take the target into/out of async mode. */ + getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p); + if (!target_is_async_p ()) + signal (SIGINT, ofunc); } - /* FIXME: cagney/1999-09-27: If we're in async mode we should - _never_ wait for ever -> test on target_is_async_p(). - However, before we do that we need to ensure that the caller - knows how to take the target into/out of async mode. */ - getpkt (&rs->buf, &rs->buf_size, wait_forever_enabled_p); - if (!target_is_async_p ()) - signal (SIGINT, ofunc); buf = rs->buf; @@ -4705,6 +4884,7 @@ putpkt (char *buf) static int putpkt_binary (char *buf, int cnt) { + struct remote_state *rs = get_remote_state (); int i; unsigned char csum = 0; char *buf2 = alloca (cnt + 6); @@ -4713,6 +4893,10 @@ putpkt_binary (char *buf, int cnt) int tcount = 0; char *p; + /* We're sending out a new packet. Make sure we don't look at a + stale cached response. */ + rs->cached_wait_status = 0; + /* Copy the packet into buffer BUF2, encapsulating it and giving it a checksum. */ @@ -5014,11 +5198,16 @@ getpkt (char **buf, static int getpkt_sane (char **buf, long *sizeof_buf, int forever) { + struct remote_state *rs = get_remote_state (); int c; int tries; int timeout; int val; + /* We're reading a new response. Make sure we don't look at a + previously cached response. */ + rs->cached_wait_status = 0; + strcpy (*buf, "timeout"); if (forever) @@ -5150,19 +5339,6 @@ remote_async_mourn (void) remote_mourn_1 (&remote_async_ops); } -static void -extended_remote_mourn (void) -{ - /* We do _not_ want to mourn the target like this; this will - remove the extended remote target from the target stack, - and the next time the user says "run" it'll fail. - - FIXME: What is the right thing to do here? */ -#if 0 - remote_mourn_1 (&extended_remote_ops); -#endif -} - /* Worker function for remote_mourn. */ static void remote_mourn_1 (struct target_ops *target) @@ -5171,71 +5347,167 @@ remote_mourn_1 (struct target_ops *targe generic_mourn_inferior (); } -/* In the extended protocol we want to be able to do things like - "run" and have them basically work as expected. So we need - a special create_inferior function. +static void +extended_remote_mourn_1 (struct target_ops *target) +{ + struct remote_state *rs = get_remote_state (); + + /* Unlike "target remote", we do not want to unpush the target; then + the next time the user says "run", we won't be connected. */ + + /* Call common code to mark the inferior as not running. */ + generic_mourn_inferior (); - FIXME: One day add support for changing the exec file - we're debugging, arguments and an environment. */ + /* Check whether the target is running now - some remote stubs + automatically restart after kill. */ + putpkt ("?"); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'S' || rs->buf[0] == 'T') + { + /* Assume that the target has been restarted. Set inferior_ptid + so that bits of core GDB realizes there's something here, e.g., + so that the user can say "kill" again. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + } + else + { + /* Mark this (still pushed) target as not executable until we + restart it. */ + target_mark_exited (target); + } +} static void -extended_remote_create_inferior (char *exec_file, char *args, - char **env, int from_tty) +extended_remote_mourn (void) +{ + extended_remote_mourn_1 (&extended_remote_ops); +} + +static void +extended_async_remote_mourn (void) { - /* Rip out the breakpoints; we'll reinsert them after restarting - the remote server. */ - remove_breakpoints (); + extended_remote_mourn_1 (&extended_async_remote_ops); +} - /* Now restart the remote server. */ - extended_remote_restart (); +static int +extended_remote_run (char *args) +{ + struct remote_state *rs = get_remote_state (); + char *p; + int len; - /* NOTE: We don't need to recheck for a target description here; but - if we gain the ability to switch the remote executable we may - need to, if for instance we are running a process which requested - different emulated hardware from the operating system. A - concrete example of this is ARM GNU/Linux, where some binaries - will have a legacy FPA coprocessor emulated and others may have - access to a hardware VFP unit. */ - - /* Now put the breakpoints back in. This way we're safe if the - restart function works via a unix fork on the remote side. */ - insert_breakpoints (); + /* If the user has disabled vRun support, or we have detected that + support is not available, do not try it. */ + if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE) + return -1; - /* Clean up from the last time we were running. */ - clear_proceed_status (); + strcpy (rs->buf, "vRun;"); + len = strlen (rs->buf); + + if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ()) + error (_("Remote file name too long for run packet")); + len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf + len, 0); + + if (*args) + { + struct cleanup *back_to; + int i; + char **argv; + + argv = buildargv (args); + back_to = make_cleanup ((void (*) (void *)) freeargv, argv); + for (i = 0; argv[i] != NULL; i++) + { + if (strlen (argv[i]) * 2 + 1 + len >= get_remote_packet_size ()) + error (_("Argument list too long for run packet")); + rs->buf[len++] = ';'; + len += 2 * bin2hex ((gdb_byte *) argv[i], rs->buf + len, 0); + } + do_cleanups (back_to); + } + + rs->buf[len++] = '\0'; + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (packet_ok (rs->buf, &remote_protocol_packets[PACKET_vRun]) == PACKET_OK) + { + /* We have a wait response; we don't need it, though. All is well. */ + return 0; + } + else if (remote_protocol_packets[PACKET_vRun].support == PACKET_DISABLE) + /* It wasn't disabled before, but it is now. */ + return -1; + else + { + if (remote_exec_file[0] == '\0') + error (_("Running the default executable on the remote target failed; " + "try \"set remote exec-file\"?")); + else + error (_("Running \"%s\" on the remote target failed"), + remote_exec_file); + } } -/* Async version of extended_remote_create_inferior. */ +/* In the extended protocol we want to be able to do things like + "run" and have them basically work as expected. So we need + a special create_inferior function. We support changing the + executable file and the command line arguments, but not the + environment. */ + static void -extended_remote_async_create_inferior (char *exec_file, char *args, - char **env, int from_tty) +extended_remote_create_inferior_1 (char *exec_file, char *args, + char **env, int from_tty, + int async_p) { - /* Rip out the breakpoints; we'll reinsert them after restarting - the remote server. */ - remove_breakpoints (); - /* If running asynchronously, register the target file descriptor with the event loop. */ - if (target_can_async_p ()) + if (async_p && target_can_async_p ()) target_async (inferior_event_handler, 0); /* Now restart the remote server. */ - extended_remote_restart (); + if (extended_remote_run (args) == -1) + { + /* vRun was not supported. Fail if we need it to do what the + user requested. */ + if (remote_exec_file[0]) + error (_("Remote target does not support \"set remote exec-file\"")); + if (args[0]) + error (_("Remote target does not support \"set args\" or run <ARGS>")); + + /* Fall back to "R". */ + extended_remote_restart (); + } - /* NOTE: We don't need to recheck for a target description here; but - if we gain the ability to switch the remote executable we may - need to, if for instance we are running a process which requested - different emulated hardware from the operating system. A - concrete example of this is ARM GNU/Linux, where some binaries - will have a legacy FPA coprocessor emulated and others may have - access to a hardware VFP unit. */ - - /* Now put the breakpoints back in. This way we're safe if the - restart function works via a unix fork on the remote side. */ - insert_breakpoints (); + /* Now mark the inferior as running before we do anything else. */ + inferior_ptid = pid_to_ptid (MAGIC_NULL_PID); + if (async_p) + target_mark_running (&extended_async_remote_ops); + else + target_mark_running (&extended_remote_ops); + + /* Get updated offsets, if the stub uses qOffsets. */ + get_offsets (); /* Clean up from the last time we were running. */ - clear_proceed_status (); + init_thread_list (); + init_wait_for_inferior (); +} + +static void +extended_remote_create_inferior (char *exec_file, char *args, + char **env, int from_tty) +{ + extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 0); +} + +static void +extended_remote_async_create_inferior (char *exec_file, char *args, + char **env, int from_tty) +{ + extended_remote_create_inferior_1 (exec_file, args, env, from_tty, 1); } \f @@ -5793,6 +6065,12 @@ remote_xfer_partial (struct target_ops * int xfered; errno = 0; + /* If the remote target is connected but not running, we should + pass this request down to a lower stratum (e.g. the executable + file). */ + if (!target_has_execution) + return 0; + if (writebuf != NULL) xfered = remote_write_bytes (offset, writebuf, len); else @@ -6994,6 +7272,8 @@ Specify the serial device it is connecte extended_remote_ops.to_open = extended_remote_open; extended_remote_ops.to_create_inferior = extended_remote_create_inferior; extended_remote_ops.to_mourn_inferior = extended_remote_mourn; + extended_remote_ops.to_detach = extended_remote_detach; + extended_remote_ops.to_attach = extended_remote_attach; } static int @@ -7126,7 +7406,9 @@ init_extended_async_remote_ops (void) Specify the serial device it is connected to (e.g. /dev/ttya).", extended_async_remote_ops.to_open = extended_remote_async_open; extended_async_remote_ops.to_create_inferior = extended_remote_async_create_inferior; - extended_async_remote_ops.to_mourn_inferior = extended_remote_mourn; + extended_async_remote_ops.to_mourn_inferior = extended_async_remote_mourn; + extended_async_remote_ops.to_detach = extended_remote_detach; + extended_async_remote_ops.to_attach = extended_async_remote_attach; } static void @@ -7381,6 +7663,12 @@ Show the maximum size of the address (in add_packet_config_cmd (&remote_protocol_packets[PACKET_vFile_unlink], "vFile:unlink", "hostio-unlink", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_vAttach], + "vAttach", "attach", 0); + + add_packet_config_cmd (&remote_protocol_packets[PACKET_vRun], + "vRun", "run", 0); + /* Keep the old ``set remote Z-packet ...'' working. Each individual Z sub-packet has its own set and show commands, but users may have sets to this variable in their .gdbinit files (or in their @@ -7413,6 +7701,13 @@ Transfer files to and from the remote ta _("Delete a remote file."), &remote_cmdlist); + remote_exec_file = xstrdup (""); + add_setshow_string_noescape_cmd ("exec-file", class_files, + &remote_exec_file, _("\ +Set the remote pathname for \"run\""), _("\ +Show the remote pathname for \"run\""), NULL, NULL, NULL, + &remote_set_cmdlist, &remote_show_cmdlist); + /* Eventually initialize fileio. See fileio.c */ initialize_remote_fileio (remote_set_cmdlist, remote_show_cmdlist); } Index: doc/gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.460 diff -u -p -r1.460 gdb.texinfo --- doc/gdb.texinfo 26 Jan 2008 19:14:18 -0000 1.460 +++ doc/gdb.texinfo 30 Jan 2008 00:16:34 -0000 @@ -12947,9 +12947,19 @@ choice for debugging. or a TCP connection, using the standard @value{GDBN} remote serial protocol. -@table @emph -@item On the target machine, -you need to have a copy of the program you want to debug. +@quotation +@emph{Warning:} @code{gdbserver} does not have any built-in security. +Do not run @code{gdbserver} connected to any public network; a +@value{GDBN} connection to @code{gdbserver} provides access to the +target system with the same privileges as the user running +@code{gdbserver}. +@end quotation + +@subsection Running @code{gdbserver} +@cindex arguments, to @code{gdbserver} + +Run @code{gdbserver} on the target system. You need a copy of the +program you want to debug, including any libraries it requires. @code{gdbserver} does not need your program's symbol table, so you can strip the program if necessary to save space. @value{GDBN} on the host system does all the symbol handling. @@ -12992,11 +13002,13 @@ conflicts with another service, @code{gd and exits.} You must use the same port number with the host @value{GDBN} @code{target remote} command. +@subsubsection Attaching to a Running Program + On some targets, @code{gdbserver} can also attach to running programs. This is accomplished via the @code{--attach} argument. The syntax is: @smallexample -target> gdbserver @var{comm} --attach @var{pid} +target> gdbserver --attach @var{comm} @var{pid} @end smallexample @var{pid} is the process ID of a currently running process. It isn't necessary @@ -13008,18 +13020,56 @@ You can debug processes by name instead @code{pidof} utility: @smallexample -target> gdbserver @var{comm} --attach `pidof @var{program}` +target> gdbserver --attach @var{comm} `pidof @var{program}` @end smallexample In case more than one copy of @var{program} is running, or @var{program} has multiple threads, most versions of @code{pidof} support the @code{-s} option to only return the first process ID. -@item On the host machine, -first make sure you have the necessary symbol files. Load symbols for +@subsubsection Multi-Process Mode for @code{gdbserver} +@cindex gdbserver, multiple processes +@cindex multiple processes with gdbserver + +When you connect to @code{gdbserver} using @code{target remote}, +@code{gdbserver} debugs the specified program only once. When the +program exits, or you detach from it, @value{GDBN} closes the connection +and @code{gdbserver} exits. + +If you connect using @code{target extended-remote}, @code{gdbserver} +enters multi-process mode. When the debugged program exits, or you +detach from it, @value{GDBN} stays connected to @code{gdbserver} even +though no program is running. The @code{run} and @code{attach} +commands instruct @code{gdbserver} to run or attach to a new program. +The @code{run} command uses @code{set remote exec-file} (@pxref{set +remote exec-file}) to select the program to run. Command line +arguments are supported, except for wildcard expansion and I/O +redirection (@pxref{Arguments}). + +To start @code{gdbserver} without supplying an initial command to run +or process ID to attach, use the @option{--multi} command line option. +Then you can connect using @code{target extended-remote} and start +the program you want to debug. + +@code{gdbserver} does not automatically exit in multi-process mode. +You can terminate it by using @code{monitor exit} +(@pxref{Monitor Commands for gdbserver}). + +@subsubsection Other Command-Line Arguments for @code{gdbserver} + +You can include @option{--debug} on the @code{gdbserver} command line. +@code{gdbserver} will display extra status information about the debugging +process. This option is intended for @code{gdbserver} development and +for bug reports to the developers. + +@subsection Connecting to @code{gdbserver} + +Run @value{GDBN} on the host system. + +First make sure you have the necessary symbol files. Load symbols for your application using the @code{file} command before you connect. Use @code{set sysroot} to locate target libraries (unless your @value{GDBN} -was compiled with the correct sysroot using @code{--with-system-root}). +was compiled with the correct sysroot using @code{--with-sysroot}). The symbol file and target libraries must exactly match the executable and libraries on the target, with one exception: the files on the host @@ -13033,19 +13083,17 @@ Connect to your target (@pxref{Connectin For TCP connections, you must start up @code{gdbserver} prior to using the @code{target remote} command. Otherwise you may get an error whose text depends on the host system, but which usually looks something like -@samp{Connection refused}. You don't need to use the @code{load} +@samp{Connection refused}. Don't use the @code{load} command in @value{GDBN} when using @code{gdbserver}, since the program is already on the target. -@end table - @subsection Monitor Commands for @code{gdbserver} @cindex monitor commands, for @code{gdbserver} +@anchor{Monitor Commands for gdbserver} During a @value{GDBN} session using @code{gdbserver}, you can use the @code{monitor} command to send special requests to @code{gdbserver}. -Here are the available commands; they are only of interest when -debugging @value{GDBN} or @code{gdbserver}. +Here are the available commands. @table @code @item monitor help @@ -13060,6 +13108,13 @@ Disable or enable general debugging mess Disable or enable specific debugging messages associated with the remote protocol (@pxref{Remote Protocol}). +@item monitor exit +Tell gdbserver to exit immediately. This command should be followed by +@code{disconnect} to close the debugging session. @code{gdbserver} will +detach from any attached processes and kill any processes it created. +Use @code{monitor exit} to terminate @code{gdbserver} at the end +of a multi-process mode debug session. + @end table @node Remote Configuration @@ -13154,6 +13209,15 @@ responses. @itemx set remote hardware-breakpoint-limit @var{limit} Restrict @value{GDBN} to using @var{limit} remote hardware breakpoint or watchpoints. A limit of -1, the default, is treated as unlimited. + +@item set remote exec-file @var{filename} +@itemx show remote exec-file +@anchor{set remote exec-file} +@cindex executable file, for remote target +Select the file used for @code{run} with @code{target +extended-remote}. This should be set to a filename valid on the +target system. If it is not set, the target will use a default +filename (e.g.@: the last program run). @end table @cindex remote packets, enabling and disabling @@ -13200,10 +13264,18 @@ are: @tab @code{qSymbol} @tab Detecting multiple threads +@item @code{attach} +@tab @code{vAttach} +@tab @code{attach} + @item @code{verbose-resume} @tab @code{vCont} @tab Stepping or resuming multiple threads +@item @code{run} +@tab @code{vRun} +@tab @code{run} + @item @code{software-breakpoint} @tab @code{Z0} @tab @code{break} @@ -23293,6 +23365,7 @@ Here are the packet descriptions. @item ! @cindex @samp{!} packet +@anchor{extended mode} Enable extended mode. In extended mode, the remote server is made persistent. The @samp{R} packet is used to restart the program being debugged. @@ -23558,7 +23631,7 @@ Don't use this packet; use the @samp{R} @item R @var{XX} @cindex @samp{R} packet Restart the program being debugged. @var{XX}, while needed, is ignored. -This packet is only available in extended mode. +This packet is only available in extended mode (@pxref{extended mode}). The @samp{R} packet has no reply. @@ -23601,6 +23674,22 @@ thread is dead Packets starting with @samp{v} are identified by a multi-letter name, up to the first @samp{;} or @samp{?} (or the end of the packet). +@item vAttach;@var{pid} +@cindex @samp{vAttach} packet +Attach to a new process with the specified process ID. @var{pid} is a +hexadecimal integer identifying the process. If the stub is currently +controlling a process, it is killed. The attached process is stopped. + +This packet is only available in extended mode (@pxref{extended mode}). + +Reply: +@table @samp +@item E @var{nn} +for an error +@item @r{Any stop packet} +for success (@pxref{Stop Reply Packets}) +@end table + @item vCont@r{[};@var{action}@r{[}:@var{tid}@r{]]}@dots{} @cindex @samp{vCont} packet Resume the inferior, specifying different actions for each thread. @@ -23697,6 +23786,24 @@ The stub is permitted to delay or batch regions of flash memory are unpredictable until the @samp{vFlashDone} request is completed. +@item vRun;@var{filename}@r{[};@var{argument}@r{]}@dots{} +@cindex @samp{vRun} packet +Run the program @var{filename}, passing it each @var{argument} on its +command line. The file and arguments are hex-encoded strings. If +@var{filename} is an empty string, the stub may use a default program +(e.g.@: the last program run). The program is created in the stopped +state. If the stub is currently controlling a process, it is killed. + +This packet is only available in extended mode (@pxref{extended mode}). + +Reply: +@table @samp +@item E @var{nn} +for an error +@item @r{Any stop packet} +for success (@pxref{Stop Reply Packets}) +@end table + @item X @var{addr},@var{length}:@var{XX@dots{}} @anchor{X packet} @cindex @samp{X} packet Index: gdbserver/README =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/README,v retrieving revision 1.3 diff -u -p -r1.3 README --- gdbserver/README 5 Dec 2006 21:18:38 -0000 1.3 +++ gdbserver/README 30 Jan 2008 00:16:34 -0000 @@ -9,6 +9,8 @@ host. GDB and GDBserver communicate usi implemented in remote.c, and various *-stub.c files. They communicate via either a serial line or a TCP connection. +For more information about GDBserver, see the GDB manual. + Usage (server (target) side): First, you need to have a copy of the program you want to debug put onto @@ -47,7 +49,7 @@ print an error message and exit. On some targets, gdbserver can also attach to running programs. This is accomplished via the --attach argument. The syntax is: - target> gdbserver COMM --attach PID + target> gdbserver --attach COMM PID PID is the process ID of a currently running process. It isn't necessary to point gdbserver at a binary for the running process. Index: gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.70 diff -u -p -r1.70 linux-low.c --- gdbserver/linux-low.c 1 Jan 2008 22:53:14 -0000 1.70 +++ gdbserver/linux-low.c 30 Jan 2008 00:16:34 -0000 @@ -304,14 +304,18 @@ linux_attach_lwp (unsigned long pid) if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0) { - fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + if (all_threads.head != NULL) + { + /* If we fail to attach to an LWP, just warn. */ + fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid, + strerror (errno), errno); + fflush (stderr); + return; + } + else + /* If we fail to attach to a process, report an error. */ + error ("Cannot attach to process %ld: %s (%d)\n", pid, strerror (errno), errno); - fflush (stderr); - - /* If we fail to attach to an LWP, just return. */ - if (all_threads.head == NULL) - _exit (0177); - return; } ptrace (PTRACE_SETOPTIONS, pid, 0, PTRACE_O_TRACECLONE); @@ -396,6 +400,10 @@ linux_kill (void) /* Make sure it died. The loop is most likely unnecessary. */ wstat = linux_wait_for_event (thread); } while (WIFSTOPPED (wstat)); + + clear_inferiors (); + free (all_processes.head); + all_processes.head = all_processes.tail = NULL; } static void @@ -434,6 +442,8 @@ linux_detach (void) delete_all_breakpoints (); for_each_inferior (&all_threads, linux_detach_one_process); clear_inferiors (); + free (all_processes.head); + all_processes.head = all_processes.tail = NULL; return 0; } Index: gdbserver/remote-utils.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v retrieving revision 1.53 diff -u -p -r1.53 remote-utils.c --- gdbserver/remote-utils.c 1 Jan 2008 22:53:14 -0000 1.53 +++ gdbserver/remote-utils.c 30 Jan 2008 00:16:34 -0000 @@ -187,15 +187,15 @@ remote_open (char *name) #ifdef USE_WIN32API static int winsock_initialized; #endif - char *port_str; int port; struct sockaddr_in sockaddr; socklen_t tmp; int tmp_desc; + char *port_end; - port_str = strchr (name, ':'); - - port = atoi (port_str + 1); + port = strtoul (port_str + 1, &port_end, 10); + if (port_str[1] == '\0' || *port_end != '\0') + fatal ("Bad port argument: %s", name); #ifdef USE_WIN32API if (!winsock_initialized) @@ -575,7 +575,7 @@ putpkt_binary (char *buf, int cnt) } /* Check for an input interrupt while we're here. */ - if (buf3[0] == '\003') + if (buf3[0] == '\003' && current_inferior != NULL) (*the_target->request_interrupt) (); } while (buf3[0] != '+'); @@ -617,7 +617,7 @@ input_interrupt (int unused) cc = read (remote_desc, &c, 1); - if (cc != 1 || c != '\003') + if (cc != 1 || c != '\003' || current_inferior == NULL) { fprintf (stderr, "input_interrupt, count = %d c = %d ('%c')\n", cc, c, c); Index: gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.62 diff -u -p -r1.62 server.c --- gdbserver/server.c 1 Jan 2008 22:53:14 -0000 1.62 +++ gdbserver/server.c 30 Jan 2008 00:16:35 -0000 @@ -34,9 +34,15 @@ unsigned long general_thread; unsigned long step_thread; unsigned long thread_from_wait; unsigned long old_thread_from_wait; -int extended_protocol; int server_waiting; +static int extended_protocol; +static int attached; +static int response_needed; +static int exit_requested; + +static char **program_argv; + /* Enable miscellaneous debugging output. The name is historical - it was originally used to debug LinuxThreads support. */ int debug_threads; @@ -69,8 +75,16 @@ restore_old_foreground_pgrp (void) #endif static int +target_running (void) +{ + return all_threads.head != NULL; +} + +static int start_inferior (char *argv[], char *statusptr) { + attached = 0; + #ifdef SIGTTOU signal (SIGTTOU, SIG_DFL); signal (SIGTTIN, SIG_DFL); @@ -107,6 +121,8 @@ attach_inferior (int pid, char *statuspt if (myattach (pid) != 0) return -1; + attached = 1; + fprintf (stderr, "Attached; pid = %d\n", pid); fflush (stderr); @@ -254,6 +270,13 @@ monitor_show_help (void) monitor_output (" Enable remote protocol debugging messages\n"); } +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + return; \ + } + /* Handle all of the extended 'q' packets. */ void handle_query (char *own_buf, int packet_len, int *new_packet_len_p) @@ -263,6 +286,7 @@ handle_query (char *own_buf, int packet_ /* Reply the current thread id. */ if (strcmp ("qC", own_buf) == 0) { + require_running (own_buf); thread_ptr = all_threads.head; sprintf (own_buf, "QC%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); @@ -271,7 +295,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qSymbol::", own_buf) == 0) { - if (the_target->look_up_symbols != NULL) + if (target_running () && the_target->look_up_symbols != NULL) (*the_target->look_up_symbols) (); strcpy (own_buf, "OK"); @@ -280,6 +304,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qfThreadInfo", own_buf) == 0) { + require_running (own_buf); thread_ptr = all_threads.head; sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); thread_ptr = thread_ptr->next; @@ -288,6 +313,7 @@ handle_query (char *own_buf, int packet_ if (strcmp ("qsThreadInfo", own_buf) == 0) { + require_running (own_buf); if (thread_ptr != NULL) { sprintf (own_buf, "m%x", thread_to_gdb_id ((struct thread_info *)thread_ptr)); @@ -305,7 +331,8 @@ handle_query (char *own_buf, int packet_ && strcmp ("qOffsets", own_buf) == 0) { CORE_ADDR text, data; - + + require_running (own_buf); if (the_target->read_offsets (&text, &data)) sprintf (own_buf, "Text=%lX;Data=%lX;Bss=%lX", (long)text, (long)data, (long)data); @@ -324,6 +351,7 @@ handle_query (char *own_buf, int packet_ CORE_ADDR ofs; unsigned char *spu_buf; + require_running (own_buf); strcpy (own_buf, "E00"); if (decode_xfer_read (own_buf + 15, &annex, &ofs, &len) < 0) return; @@ -356,6 +384,7 @@ handle_query (char *own_buf, int packet_ CORE_ADDR ofs; unsigned char *spu_buf; + require_running (own_buf); strcpy (own_buf, "E00"); spu_buf = malloc (packet_len - 15); if (!spu_buf) @@ -387,6 +416,8 @@ handle_query (char *own_buf, int packet_ unsigned int len; char *annex; + require_running (own_buf); + /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 16, &annex, &ofs, &len) < 0 || annex[0] != '\0') @@ -420,6 +451,8 @@ handle_query (char *own_buf, int packet_ const char *document; char *annex; + require_running (own_buf); + /* Check for support. */ document = get_features_xml ("target.xml"); if (document == NULL) @@ -467,6 +500,8 @@ handle_query (char *own_buf, int packet_ struct inferior_list_entry *dll_ptr; char *annex; + require_running (own_buf); + /* Reject any annex; grab the offset and length. */ if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0 || annex[0] != '\0') @@ -535,7 +570,7 @@ handle_query (char *own_buf, int packet_ if (the_target->read_auxv != NULL) strcat (own_buf, ";qXfer:auxv:read+"); - + if (the_target->qxfer_spu != NULL) strcat (own_buf, ";qXfer:spu:read+;qXfer:spu:write+"); @@ -553,6 +588,8 @@ handle_query (char *own_buf, int packet_ CORE_ADDR parts[3], address = 0; int i, err; + require_running (own_buf); + for (i = 0; i < 3; i++) { char *p2; @@ -642,6 +679,8 @@ handle_query (char *own_buf, int packet_ } else if (strcmp (mon, "help") == 0) monitor_show_help (); + else if (strcmp (mon, "exit") == 0) + exit_requested = 1; else { monitor_output ("Unknown monitor command.\n\n"); @@ -772,6 +811,95 @@ err: return; } +/* Attach to a new program. Return 1 if successful, 0 if failure. */ +int +handle_v_attach (char *own_buf, char *status, int *signal) +{ + int pid; + + pid = strtol (own_buf + 8, NULL, 16); + if (pid != 0 && attach_inferior (pid, status, signal) == 0) + { + prepare_resume_reply (own_buf, *status, *signal); + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + +/* Run a new program. Return 1 if successful, 0 if failure. */ +static int +handle_v_run (char *own_buf, char *status, int *signal) +{ + char *p, **pp, *next_p, **new_argv; + int i, new_argc; + + new_argc = 0; + for (p = own_buf + strlen ("vRun;"); p && *p; p = strchr (p, ';')) + { + p++; + new_argc++; + } + + new_argv = malloc ((new_argc + 2) * sizeof (char *)); + i = 0; + for (p = own_buf + strlen ("vRun;"); *p; p = next_p) + { + next_p = strchr (p, ';'); + if (next_p == NULL) + next_p = p + strlen (p); + + if (i == 0 && p == next_p) + new_argv[i] = NULL; + else + { + new_argv[i] = malloc (1 + (next_p - p) / 2); + unhexify (new_argv[i], p, (next_p - p) / 2); + new_argv[i][(next_p - p) / 2] = '\0'; + } + + if (*next_p) + next_p++; + i++; + } + new_argv[i] = NULL; + + if (new_argv[0] == NULL) + { + if (program_argv == NULL) + { + write_enn (own_buf); + return 0; + } + + new_argv[0] = strdup (program_argv[0]); + } + + /* Free the old argv. */ + if (program_argv) + { + for (pp = program_argv; *pp != NULL; pp++) + free (*pp); + free (program_argv); + } + program_argv = new_argv; + + *signal = start_inferior (program_argv, status); + if (*status == 'T') + { + prepare_resume_reply (own_buf, *status, *signal); + return 1; + } + else + { + write_enn (own_buf); + return 0; + } +} + /* Handle all of the extended 'v' packets. */ void handle_v_requests (char *own_buf, char *status, int *signal, @@ -779,6 +907,7 @@ handle_v_requests (char *own_buf, char * { if (strncmp (own_buf, "vCont;", 6) == 0) { + require_running (own_buf); handle_v_cont (own_buf, status, signal); return; } @@ -793,6 +922,28 @@ handle_v_requests (char *own_buf, char * && handle_vFile (own_buf, packet_len, new_packet_len)) return; + if (strncmp (own_buf, "vAttach;", 8) == 0) + { + if (target_running ()) + { + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + } + handle_v_attach (own_buf, status, signal); + return; + } + + if (strncmp (own_buf, "vRun;", 5) == 0) + { + if (target_running ()) + { + fprintf (stderr, "Killing inferior\n"); + kill_inferior (); + } + handle_v_run (own_buf, status, signal); + return; + } + /* Otherwise we didn't know what packet it was. Say we didn't understand it. */ own_buf[0] = 0; @@ -829,8 +980,6 @@ myresume (char *own_buf, int step, int * disable_async_io (); } -static int attached; - static void gdbserver_version (void) { @@ -844,13 +993,25 @@ gdbserver_version (void) static void gdbserver_usage (void) { - printf ("Usage:\tgdbserver COMM PROG [ARGS ...]\n" - "\tgdbserver COMM --attach PID\n" + printf ("Usage:\tgdbserver [OPTIONS] COMM PROG [ARGS ...]\n" + "\tgdbserver [OPTIONS] --attach COMM PID\n" + "\tgdbserver [OPTIONS] --multi COMM\n" "\n" "COMM may either be a tty device (for serial debugging), or \n" - "HOST:PORT to listen for a TCP connection.\n"); + "HOST:PORT to listen for a TCP connection.\n" + "\n" + "Options:\n" + " --debug\t\tEnable debugging output.\n"); } +#undef require_running +#define require_running(BUF) \ + if (!target_running ()) \ + { \ + write_enn (BUF); \ + break; \ + } + int main (int argc, char *argv[]) { @@ -862,18 +1023,38 @@ main (int argc, char *argv[]) CORE_ADDR mem_addr; int bad_attach; int pid; - char *arg_end; + char *arg_end, *port; + char **next_arg = &argv[1]; + int multi_mode = 0; + int attach = 0; + int was_running; - if (argc >= 2 && strcmp (argv[1], "--version") == 0) + while (*next_arg != NULL && **next_arg == '-') { - gdbserver_version (); - exit (0); - } + if (strcmp (*next_arg, "--version") == 0) + { + gdbserver_version (); + exit (0); + } + else if (strcmp (*next_arg, "--help") == 0) + { + gdbserver_usage (); + exit (0); + } + else if (strcmp (*next_arg, "--attach") == 0) + attach = 1; + else if (strcmp (*next_arg, "--multi") == 0) + multi_mode = 1; + else if (strcmp (*next_arg, "--debug") == 0) + debug_threads = 1; + else + { + fprintf (stderr, "Unknown argument: %s\n", *next_arg); + exit (1); + } - if (argc >= 2 && strcmp (argv[1], "--help") == 0) - { - gdbserver_usage (); - exit (0); + next_arg++; + continue; } if (setjmp (toplevel)) @@ -882,23 +1063,34 @@ main (int argc, char *argv[]) exit (1); } - bad_attach = 0; - pid = 0; - attached = 0; - if (argc >= 3 && strcmp (argv[2], "--attach") == 0) + port = *next_arg; + next_arg++; + if (port == NULL || (!attach && !multi_mode && *next_arg == NULL)) { - if (argc == 4 - && argv[3][0] != '\0' - && (pid = strtoul (argv[3], &arg_end, 0)) != 0 - && *arg_end == '\0') - { - ; - } - else - bad_attach = 1; + gdbserver_usage (); + exit (1); } - if (argc < 3 || bad_attach) + bad_attach = 0; + pid = 0; + + /* --attach used to come after PORT, so allow it there for + compatibility. */ + if (*next_arg != NULL && strcmp (*next_arg, "--attach") == 0) + { + attach = 1; + next_arg++; + } + + if (attach + && (*next_arg == NULL + || (*next_arg)[0] == '\0' + || (pid = strtoul (*next_arg, &arg_end, 0)) == 0 + || *arg_end != '\0' + || next_arg[1] != NULL)) + bad_attach = 1; + + if (bad_attach) { gdbserver_usage (); exit (1); @@ -910,26 +1102,34 @@ main (int argc, char *argv[]) own_buf = malloc (PBUFSIZ + 1); mem_buf = malloc (PBUFSIZ); - if (pid == 0) + if (pid == 0 && *next_arg != NULL) { + int i, n; + + n = argc - (next_arg - argv); + program_argv = malloc (sizeof (char *) * (n + 1)); + for (i = 0; i < n; i++) + program_argv[i] = strdup (next_arg[i]); + program_argv[i] = NULL; + /* Wait till we are at first instruction in program. */ - signal = start_inferior (&argv[2], &status); + signal = start_inferior (program_argv, &status); /* We are now (hopefully) stopped at the first instruction of the target process. This assumes that the target process was successfully created. */ } + else if (pid != 0) + { + if (attach_inferior (pid, &status, &signal) == -1) + error ("Attaching not supported on this target"); + + /* Otherwise succeeded. */ + } else { - switch (attach_inferior (pid, &status, &signal)) - { - case -1: - error ("Attaching not supported on this target"); - break; - default: - attached = 1; - break; - } + status = 'W'; + signal = 0; } /* Don't report shared library events on the initial connection, @@ -945,27 +1145,43 @@ main (int argc, char *argv[]) } if (status == 'W' || status == 'X') + was_running = 0; + else + was_running = 1; + + if (!was_running && !multi_mode) { - fprintf (stderr, "No inferior, GDBserver exiting.\n"); + fprintf (stderr, "No program to debug. GDBserver exiting.\n"); exit (1); } while (1) { - remote_open (argv[1]); + remote_open (port); restart: - setjmp (toplevel); + if (setjmp (toplevel) != 0) + { + /* An error occurred. */ + if (response_needed) + { + write_enn (own_buf); + putpkt (own_buf); + } + } + disable_async_io (); - while (1) + while (!exit_requested) { unsigned char sig; int packet_len; int new_packet_len = -1; + response_needed = 0; packet_len = getpkt (own_buf); if (packet_len <= 0) break; + response_needed = 1; i = 0; ch = own_buf[i++]; @@ -978,39 +1194,38 @@ main (int argc, char *argv[]) handle_general_set (own_buf); break; case 'D': + require_running (own_buf); fprintf (stderr, "Detaching from inferior\n"); if (detach_inferior () != 0) - { - write_enn (own_buf); - putpkt (own_buf); - } + write_enn (own_buf); else { write_ok (own_buf); - putpkt (own_buf); - remote_close (); - /* If we are attached, then we can exit. Otherwise, we - need to hang around doing nothing, until the child - is gone. */ - if (!attached) - join_inferior (); + if (extended_protocol) + { + /* Treat this like a normal program exit. */ + signal = 0; + status = 'W'; + } + else + { + putpkt (own_buf); + remote_close (); - exit (0); + /* If we are attached, then we can exit. Otherwise, we + need to hang around doing nothing, until the child + is gone. */ + if (!attached) + join_inferior (); + + exit (0); + } } + break; case '!': - if (attached == 0) - { - extended_protocol = 1; - prepare_resume_reply (own_buf, status, signal); - } - else - { - /* We can not use the extended protocol if we are - attached, because we can not restart the running - program. So return unrecognized. */ - own_buf[0] = '\0'; - } + extended_protocol = 1; + write_ok (own_buf); break; case '?': prepare_resume_reply (own_buf, status, signal); @@ -1020,12 +1235,18 @@ main (int argc, char *argv[]) { unsigned long gdb_id, thread_id; + require_running (own_buf); gdb_id = strtoul (&own_buf[2], NULL, 16); - thread_id = gdb_id_to_thread_id (gdb_id); - if (thread_id == 0) + if (gdb_id == 0 || gdb_id == -1) + thread_id = gdb_id; + else { - write_enn (own_buf); - break; + thread_id = gdb_id_to_thread_id (gdb_id); + if (thread_id == 0) + { + write_enn (own_buf); + break; + } } if (own_buf[1] == 'g') @@ -1048,15 +1269,18 @@ main (int argc, char *argv[]) } break; case 'g': + require_running (own_buf); set_desired_inferior (1); registers_to_string (own_buf); break; case 'G': + require_running (own_buf); set_desired_inferior (1); registers_from_string (&own_buf[1]); write_ok (own_buf); break; case 'm': + require_running (own_buf); decode_m_packet (&own_buf[1], &mem_addr, &len); if (read_inferior_memory (mem_addr, mem_buf, len) == 0) convert_int_to_ascii (mem_buf, own_buf, len); @@ -1064,6 +1288,7 @@ main (int argc, char *argv[]) write_enn (own_buf); break; case 'M': + require_running (own_buf); decode_M_packet (&own_buf[1], &mem_addr, &len, mem_buf); if (write_inferior_memory (mem_addr, mem_buf, len) == 0) write_ok (own_buf); @@ -1071,6 +1296,7 @@ main (int argc, char *argv[]) write_enn (own_buf); break; case 'X': + require_running (own_buf); if (decode_X_packet (&own_buf[1], packet_len - 1, &mem_addr, &len, mem_buf) < 0 || write_inferior_memory (mem_addr, mem_buf, len) != 0) @@ -1079,6 +1305,7 @@ main (int argc, char *argv[]) write_ok (own_buf); break; case 'C': + require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); @@ -1087,6 +1314,7 @@ main (int argc, char *argv[]) myresume (own_buf, 0, &signal, &status); break; case 'S': + require_running (own_buf); convert_ascii_to_int (own_buf + 1, &sig, 1); if (target_signal_to_host_p (sig)) signal = target_signal_to_host (sig); @@ -1095,10 +1323,12 @@ main (int argc, char *argv[]) myresume (own_buf, 1, &signal, &status); break; case 'c': + require_running (own_buf); signal = 0; myresume (own_buf, 0, &signal, &status); break; case 's': + require_running (own_buf); signal = 0; myresume (own_buf, 1, &signal, &status); break; @@ -1121,6 +1351,7 @@ main (int argc, char *argv[]) { int res; + require_running (own_buf); res = (*the_target->insert_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); @@ -1151,6 +1382,7 @@ main (int argc, char *argv[]) { int res; + require_running (own_buf); res = (*the_target->remove_watchpoint) (type, addr, len); if (res == 0) write_ok (own_buf); @@ -1163,20 +1395,24 @@ main (int argc, char *argv[]) break; } case 'k': + response_needed = 0; + if (!target_running ()) + /* The packet we received doesn't make sense - but we + can't reply to it, either. */ + goto restart; + fprintf (stderr, "Killing inferior\n"); kill_inferior (); - /* When using the extended protocol, we start up a new - debugging session. The traditional protocol will - exit instead. */ + + /* When using the extended protocol, we wait with no + program running. The traditional protocol will exit + instead. */ if (extended_protocol) { - write_ok (own_buf); - fprintf (stderr, "GDBserver restarting\n"); - - /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); + status = 'X'; + signal = TARGET_SIGNAL_KILL; + was_running = 0; goto restart; - break; } else { @@ -1187,6 +1423,7 @@ main (int argc, char *argv[]) { unsigned long gdb_id, thread_id; + require_running (own_buf); gdb_id = strtoul (&own_buf[1], NULL, 16); thread_id = gdb_id_to_thread_id (gdb_id); if (thread_id == 0) @@ -1202,18 +1439,25 @@ main (int argc, char *argv[]) } break; case 'R': + response_needed = 0; + /* Restarting the inferior is only supported in the extended protocol. */ if (extended_protocol) { - kill_inferior (); - write_ok (own_buf); + if (target_running ()) + kill_inferior (); fprintf (stderr, "GDBserver restarting\n"); /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); + if (program_argv != NULL) + signal = start_inferior (program_argv, &status); + else + { + status = 'X'; + signal = TARGET_SIGNAL_KILL; + } goto restart; - break; } else { @@ -1242,45 +1486,45 @@ main (int argc, char *argv[]) else putpkt (own_buf); - if (status == 'W') - fprintf (stderr, - "\nChild exited with status %d\n", signal); - if (status == 'X') - fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", - target_signal_to_host (signal), - target_signal_to_name (signal)); - if (status == 'W' || status == 'X') + response_needed = 0; + + if (was_running && (status == 'W' || status == 'X')) { - if (extended_protocol) - { - fprintf (stderr, "Killing inferior\n"); - kill_inferior (); - write_ok (own_buf); - fprintf (stderr, "GDBserver restarting\n"); + was_running = 0; - /* Wait till we are at 1st instruction in prog. */ - signal = start_inferior (&argv[2], &status); - goto restart; - break; - } + if (status == 'W') + fprintf (stderr, + "\nChild exited with status %d\n", signal); + if (status == 'X') + fprintf (stderr, "\nChild terminated with signal = 0x%x (%s)\n", + target_signal_to_host (signal), + target_signal_to_name (signal)); + + if (extended_protocol) + goto restart; else { fprintf (stderr, "GDBserver exiting\n"); exit (0); } } - } - /* We come here when getpkt fails. + if (status != 'W' && status != 'X') + was_running = 1; + } - For the extended remote protocol we exit (and this is the only - way we gracefully exit!). + /* If an exit was requested (using the "monitor exit" command), + terminate now. The only other way to get here is for + getpkt to fail; close the connection and reopen it at the + top of the loop. */ - For the traditional remote protocol close the connection, - and re-open it at the top of the loop. */ - if (extended_protocol) + if (exit_requested) { remote_close (); + if (attached && target_running ()) + detach_inferior (); + else if (target_running ()) + kill_inferior (); exit (0); } else Index: testsuite/gdb.server/ext-attach.c =================================================================== RCS file: testsuite/gdb.server/ext-attach.c diff -N testsuite/gdb.server/ext-attach.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.server/ext-attach.c 30 Jan 2008 00:16:35 -0000 @@ -0,0 +1,31 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2007 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/>. */ + +/* This program is intended to be started outside of gdb, and then + attached to by gdb. It loops for a while, but not forever. */ + +#include <unistd.h> + +int main () +{ + int i; + + for (i = 0; i < 120; i++) + sleep (1); + + return 0; +} Index: testsuite/gdb.server/ext-attach.exp =================================================================== RCS file: testsuite/gdb.server/ext-attach.exp diff -N testsuite/gdb.server/ext-attach.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.server/ext-attach.exp 30 Jan 2008 00:16:35 -0000 @@ -0,0 +1,77 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2007 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/>. + +# Test attaching to already-running programs using extended-remote. + +load_lib gdbserver-support.exp + +set testfile "ext-attach" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [skip_gdbserver_tests] } { + return 0 +} + +# On SPU, this test currently fails because "sleep" is not supported. +if { [istarget "spu*-*-*"] } { + return 0 +} + +# We need to use TCL's exec to get the pid. +if [is_remote target] then { + return 0 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + untested ext-attach.exp + return -1 +} + +gdb_exit +gdb_start +gdb_load $binfile +gdb_reinitialize_dir $srcdir/$subdir + +set target_exec [gdbserver_download] +gdbserver_start_extended + +gdb_test "set remote exec-file $target_exec" "" "set remote exec-file" + +# Start the program running and then wait for a bit, to be sure +# that it can be attached to. +set testpid [eval exec $binfile &] +exec sleep 2 +if { [istarget "*-*-cygwin*"] } { + # testpid is the Cygwin PID, GDB uses the Windows PID, which might be + # different due to the way fork/exec works. + set testpid [ exec ps -e | gawk "{ if (\$1 == $testpid) print \$4; }" ] +} + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 1" +gdb_test "backtrace" ".*main.*" "backtrace 1" + +gdb_test "detach" "Detached from remote process\\." +gdb_test "backtrace" "No stack\\." "backtrace with no program" + +gdb_test "attach $testpid" "Attached to.*" \ + "attach to remote program 2" +gdb_test "backtrace" ".*main.*" "backtrace 2" + +gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y" +gdb_test "monitor exit" "" Index: testsuite/gdb.server/ext-run.exp =================================================================== RCS file: testsuite/gdb.server/ext-run.exp diff -N testsuite/gdb.server/ext-run.exp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ testsuite/gdb.server/ext-run.exp 30 Jan 2008 00:16:35 -0000 @@ -0,0 +1,48 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2007 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/>. + +# Test running programs using extended-remote. + +load_lib gdbserver-support.exp + +set testfile "server" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} + +if { [skip_gdbserver_tests] } { + return 0 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { + return -1 +} + +gdb_exit +gdb_start +gdb_load $binfile +gdb_reinitialize_dir $srcdir/$subdir + +set target_exec [gdbserver_download] +gdbserver_start_extended + +gdb_test "set remote exec-file $target_exec" "" "set remote exec-file" + +gdb_breakpoint main +gdb_test "run" "Breakpoint.* main .*" "continue to main" + +gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y" +gdb_test "monitor exit" "" Index: testsuite/lib/gdbserver-support.exp =================================================================== RCS file: /cvs/src/src/gdb/testsuite/lib/gdbserver-support.exp,v retrieving revision 1.11 diff -u -p -r1.11 gdbserver-support.exp --- testsuite/lib/gdbserver-support.exp 29 Jan 2008 19:36:58 -0000 1.11 +++ testsuite/lib/gdbserver-support.exp 30 Jan 2008 00:16:35 -0000 @@ -132,13 +132,10 @@ proc skip_gdbserver_tests { } { return 0 } -# Start a gdbserver process running SERVER_EXEC, and connect GDB -# to it. CHILD_ARGS are passed to the inferior. -# -# Returns the target protocol and socket to connect to. +# Download the currently loaded program to the target if necessary. +# Return the target system filename. -proc gdbserver_spawn { child_args } { - global portnum +proc gdbserver_download { } { global gdbserver_host_exec global gdbserver_host_mtime global gdbserver_server_exec @@ -172,6 +169,17 @@ proc gdbserver_spawn { child_args } { } } + return $gdbserver_server_exec +} + +# Start a gdbserver process with initial OPTIONS and trailing ARGUMENTS. +# The port will be filled in between them automatically. +# +# Returns the target protocol and socket to connect to. + +proc gdbserver_start { options arguments } { + global portnum + # Port id -- either specified in baseboard file, or managed here. if [target_info exists gdb,socketport] { set portnum [target_info gdb,socketport] @@ -182,7 +190,7 @@ proc gdbserver_spawn { child_args } { # Extract the local and remote host ids from the target board struct. if [target_info exists sockethost] { - set debughost [target_info sockethost] + set debughost [target_info sockethost] } else { set debughost "localhost:" } @@ -199,23 +207,23 @@ proc gdbserver_spawn { child_args } { # Export the host:port pair. set gdbport $debughost$portnum - # Fire off the debug agent. This flavour of gdbserver takes as - # arguments the port information, the name of the executable file to - # be debugged, and any arguments. - set gdbserver_command "$gdbserver :$portnum $gdbserver_server_exec" - if { $child_args != "" } { - append gdbserver_command " $child_args" + # Fire off the debug agent. + set gdbserver_command "$gdbserver" + if { $options != "" } { + append gdbserver_command " $options" + } + append gdbserver_command " :$portnum" + if { $arguments != "" } { + append gdbserver_command " $arguments" } set server_spawn_id [remote_spawn target $gdbserver_command] - # Wait for the server to produce at least one line and an additional - # character of output. This will wait until any TCP socket has been - # created, so that GDB can connect. + # Wait for the server to open its TCP socket, so that GDB can connect. expect { -i $server_spawn_id -notransfer - -re ".*\n." { } + -re "Listening on" { } } # We can't just call close, because if gdbserver is local then that means @@ -234,6 +242,24 @@ proc gdbserver_spawn { child_args } { return [list $protocol $gdbport] } +# Start a gdbserver process running SERVER_EXEC, and connect GDB +# to it. CHILD_ARGS are passed to the inferior. +# +# Returns the target protocol and socket to connect to. + +proc gdbserver_spawn { child_args } { + set target_exec [gdbserver_download] + + # Fire off the debug agent. This flavour of gdbserver takes as + # arguments the port information, the name of the executable file to + # be debugged, and any arguments. + set arguments "$target_exec" + if { $child_args != "" } { + append arguments " $child_args" + } + return [gdbserver_start "" $arguments] +} + # Start a gdbserver process running HOST_EXEC and pass CHILD_ARGS # to it. Return 0 on success, or non-zero on failure. @@ -271,3 +297,12 @@ proc gdbserver_reconnect { } { return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] } + +# Start and connect to a gdbserver in extended mode. +proc gdbserver_start_extended { } { + set res [gdbserver_start "--multi" ""] + set gdbserver_protocol "extended-[lindex $res 0]" + set gdbserver_gdbport [lindex $res 1] + + return [gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] +} ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2008-01-30 0:55 ` Daniel Jacobowitz @ 2008-01-30 5:55 ` Eli Zaretskii 2008-01-30 13:18 ` Daniel Jacobowitz 2008-02-14 21:09 ` --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) Pedro Alves 1 sibling, 1 reply; 11+ messages in thread From: Eli Zaretskii @ 2008-01-30 5:55 UTC (permalink / raw) To: Daniel Jacobowitz; +Cc: pedro_alves, gdb-patches > Date: Tue, 29 Jan 2008 19:51:47 -0500 > From: Daniel Jacobowitz <drow@false.org> > Cc: gdb-patches@sourceware.org > > +Then you can connect using @code{target extended-remote} and start > +the program you want to debug. The command you suggest to use (target extended-remote) is something the user should type on her keyboard, so it should be in @kbd, not @code. (In general, @code is wrong for more than one word at a time.) ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [rfc] Multiple process support in gdbserver 2008-01-30 5:55 ` Eli Zaretskii @ 2008-01-30 13:18 ` Daniel Jacobowitz 0 siblings, 0 replies; 11+ messages in thread From: Daniel Jacobowitz @ 2008-01-30 13:18 UTC (permalink / raw) To: Eli Zaretskii; +Cc: pedro_alves, gdb-patches On Wed, Jan 30, 2008 at 06:20:26AM +0200, Eli Zaretskii wrote: > > Date: Tue, 29 Jan 2008 19:51:47 -0500 > > From: Daniel Jacobowitz <drow@false.org> > > Cc: gdb-patches@sourceware.org > > > > +Then you can connect using @code{target extended-remote} and start > > +the program you want to debug. > > The command you suggest to use (target extended-remote) is something > the user should type on her keyboard, so it should be in @kbd, not > @code. (In general, @code is wrong for more than one word at a time.) Thanks for the explanation; hopefully I can remember that. I checked this in to fix it. -- Daniel Jacobowitz CodeSourcery 2008-01-30 Daniel Jacobowitz <dan@codesourcery.com> * gdb.texinfo (Multi-Process Mode for gdbserver): Use @kbd for commands. Index: gdb.texinfo =================================================================== RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v retrieving revision 1.464 diff -u -p -r1.464 gdb.texinfo --- gdb.texinfo 30 Jan 2008 13:11:15 -0000 1.464 +++ gdb.texinfo 30 Jan 2008 13:12:59 -0000 @@ -13044,7 +13044,7 @@ When you connect to @code{gdbserver} usi program exits, or you detach from it, @value{GDBN} closes the connection and @code{gdbserver} exits. -If you connect using @code{target extended-remote}, @code{gdbserver} +If you connect using @kbd{target extended-remote}, @code{gdbserver} enters multi-process mode. When the debugged program exits, or you detach from it, @value{GDBN} stays connected to @code{gdbserver} even though no program is running. The @code{run} and @code{attach} @@ -13056,7 +13056,7 @@ redirection (@pxref{Arguments}). To start @code{gdbserver} without supplying an initial command to run or process ID to attach, use the @option{--multi} command line option. -Then you can connect using @code{target extended-remote} and start +Then you can connect using @kbd{target extended-remote} and start the program you want to debug. @code{gdbserver} does not automatically exit in multi-process mode. ^ permalink raw reply [flat|nested] 11+ messages in thread
* --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) 2008-01-30 0:55 ` Daniel Jacobowitz 2008-01-30 5:55 ` Eli Zaretskii @ 2008-02-14 21:09 ` Pedro Alves 2008-02-14 21:15 ` Daniel Jacobowitz 1 sibling, 1 reply; 11+ messages in thread From: Pedro Alves @ 2008-02-14 21:09 UTC (permalink / raw) To: gdb-patches [-- Attachment #1: Type: text/plain, Size: 710 bytes --] A Wednesday 30 January 2008 00:51:47, Daniel Jacobowitz wrote: > On Thu, Jan 24, 2008 at 01:50:01AM +0000, Pedro Alves wrote: > > In addition to that, this almost works on Windows targets, we just > > need to clean up the state when the inferiors are detached or > > killed, like you're doing on linux. If you prefer, I can post a > > patch once this goes in. > > Great! Yes, please do. Here it is. Basically, it's just clearing state and freeing heap objects, since without --multi we didn't care much. Tested on i686-pc-cygwin with a local gdbserver, no regressions. Without this patch, --multi will only work the first run, the following runs trip on variables with garbage on them. -- Pedro Alves [-- Attachment #2: win32_gdbserver_multi_process.diff --] [-- Type: text/x-diff, Size: 5525 bytes --] 2008-02-14 Pedro Alves <pedro_alves@portugalmail.pt> * win32-low.c (do_initial_child_stuff): Add process handle parameter. Set current_process_handle and current_process_id from the parameters. Clear globals. (win32_create_inferior): Don't set current_process_handle and current_process_id here. Instead pass them on the call to do_initial_child_stuff. (win32_attach): Likewise. (win32_clear_inferiors): New. (win32_kill): Don't close the current process handle or the current thread handle here. Instead call win32_clear_inferiors. (win32_detach): Don't open a new handle to the process. Call win32_clear_inferiors. (win32_join): Don't rely on current_process_handle; open a new handle using the process id. (win32_wait): Call win32_clear_inferiors when the inferior process has exited. --- gdb/gdbserver/win32-low.c | 78 +++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 45 deletions(-) Index: src/gdb/gdbserver/win32-low.c =================================================================== --- src.orig/gdb/gdbserver/win32-low.c 2008-02-08 01:02:18.000000000 +0000 +++ src/gdb/gdbserver/win32-low.c 2008-02-14 20:53:56.000000000 +0000 @@ -69,6 +69,7 @@ int using_threads = 1; static int attaching = 0; static HANDLE current_process_handle = NULL; static DWORD current_process_id = 0; +static DWORD main_thread_id = 0; static enum target_signal last_sig = TARGET_SIGNAL_0; /* The current debug event from WaitForDebugEvent. */ @@ -89,8 +90,6 @@ typedef BOOL WINAPI (*winapi_DebugSetPro typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE); typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD); -static DWORD main_thread_id = 0; - static void win32_resume (struct thread_resume *resume_info); /* Get the thread ID from the current selected inferior (the current @@ -290,10 +289,17 @@ child_init_thread_list (void) } static void -do_initial_child_stuff (DWORD pid) +do_initial_child_stuff (HANDLE proch, DWORD pid) { last_sig = TARGET_SIGNAL_0; + current_process_handle = proch; + current_process_id = pid; + main_thread_id = 0; + + soft_interrupt_requested = 0; + faked_breakpoint = 0; + memset (¤t_event, 0, sizeof (current_event)); child_init_thread_list (); @@ -573,10 +579,7 @@ win32_create_inferior (char *program, ch CloseHandle (pi.hThread); #endif - current_process_handle = pi.hProcess; - current_process_id = pi.dwProcessId; - - do_initial_child_stuff (current_process_id); + do_initial_child_stuff (pi.hProcess, pi.dwProcessId); return current_process_id; } @@ -607,9 +610,7 @@ win32_attach (unsigned long pid) /* win32_wait needs to know we're attaching. */ attaching = 1; - current_process_handle = h; - current_process_id = pid; - do_initial_child_stuff (pid); + do_initial_child_stuff (h, pid); return 0; } @@ -666,12 +667,20 @@ handle_output_debug_string (struct targe #undef READ_BUFFER_LEN } +static void +win32_clear_inferiors (void) +{ + if (current_process_handle != NULL) + CloseHandle (current_process_handle); + + for_each_inferior (&all_threads, delete_thread_info); + clear_inferiors (); +} + /* Kill all inferiors. */ static void win32_kill (void) { - win32_thread_info *current_thread; - if (current_process_handle == NULL) return; @@ -691,22 +700,13 @@ win32_kill (void) } } - CloseHandle (current_process_handle); - - current_thread = inferior_target_data (current_inferior); - if (current_thread && current_thread->h) - { - /* This may fail in an attached process, so don't check. */ - (void) CloseHandle (current_thread->h); - } + win32_clear_inferiors (); } /* Detach from all inferiors. */ static int win32_detach (void) { - HANDLE h; - winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; #ifdef _WIN32_WCE @@ -721,16 +721,6 @@ win32_detach (void) || DebugActiveProcessStop == NULL) return -1; - /* We need a new handle, since DebugActiveProcessStop - closes all the ones that came through the events. */ - if ((h = OpenProcess (PROCESS_ALL_ACCESS, - FALSE, - current_process_id)) == NULL) - { - /* The process died. */ - return -1; - } - { struct thread_resume resume; resume.thread = -1; @@ -741,13 +731,11 @@ win32_detach (void) } if (!DebugActiveProcessStop (current_process_id)) - { - CloseHandle (h); - return -1; - } + return -1; + DebugSetProcessKillOnExit (FALSE); - current_process_handle = h; + win32_clear_inferiors (); return 0; } @@ -755,15 +743,14 @@ win32_detach (void) static void win32_join (void) { - if (current_process_id == 0 - || current_process_handle == NULL) - return; - - WaitForSingleObject (current_process_handle, INFINITE); - CloseHandle (current_process_handle); + extern unsigned long signal_pid; - current_process_handle = NULL; - current_process_id = 0; + HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, signal_pid); + if (h != NULL) + { + WaitForSingleObject (h, INFINITE); + CloseHandle (h); + } } /* Return 1 iff the thread with thread ID TID is alive. */ @@ -1546,6 +1533,7 @@ win32_wait (char *status) our_status.value.integer)); *status = 'W'; + win32_clear_inferiors (); return our_status.value.integer; case TARGET_WAITKIND_STOPPED: case TARGET_WAITKIND_LOADED: ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) 2008-02-14 21:09 ` --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) Pedro Alves @ 2008-02-14 21:15 ` Daniel Jacobowitz 2008-02-14 22:43 ` Pedro Alves 0 siblings, 1 reply; 11+ messages in thread From: Daniel Jacobowitz @ 2008-02-14 21:15 UTC (permalink / raw) To: Pedro Alves; +Cc: gdb-patches On Thu, Feb 14, 2008 at 09:08:55PM +0000, Pedro Alves wrote: > A Wednesday 30 January 2008 00:51:47, Daniel Jacobowitz wrote: > > On Thu, Jan 24, 2008 at 01:50:01AM +0000, Pedro Alves wrote: > > > > In addition to that, this almost works on Windows targets, we just > > > need to clean up the state when the inferiors are detached or > > > killed, like you're doing on linux. If you prefer, I can post a > > > patch once this goes in. > > > > Great! Yes, please do. > > Here it is. Basically, it's just clearing state and freeing heap > objects, since without --multi we didn't care much. Tested on > i686-pc-cygwin with a local gdbserver, no regressions. Without > this patch, --multi will only work the first run, the following > runs trip on variables with garbage on them. Looks OK to me. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) 2008-02-14 21:15 ` Daniel Jacobowitz @ 2008-02-14 22:43 ` Pedro Alves 0 siblings, 0 replies; 11+ messages in thread From: Pedro Alves @ 2008-02-14 22:43 UTC (permalink / raw) To: gdb-patches A Thursday 14 February 2008 21:15:31, Daniel Jacobowitz wrote: > Looks OK to me. Thanks. Checked in. -- Pedro Alves ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2008-02-14 22:43 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2007-12-08 10:54 [rfc] Multiple process support in gdbserver Daniel Jacobowitz 2007-12-08 15:29 ` Eli Zaretskii 2007-12-08 18:14 ` Daniel Jacobowitz 2007-12-10 15:34 ` Daniel Jacobowitz 2008-01-24 1:50 ` Pedro Alves 2008-01-30 0:55 ` Daniel Jacobowitz 2008-01-30 5:55 ` Eli Zaretskii 2008-01-30 13:18 ` Daniel Jacobowitz 2008-02-14 21:09 ` --multi support for Windows targets (Re: [rfc] Multiple process support in gdbserver) Pedro Alves 2008-02-14 21:15 ` Daniel Jacobowitz 2008-02-14 22:43 ` Pedro Alves
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox