+2009-07-02 Pedro Alves <pedro@codesourcery.com>
+
+ * linux-nat.c (linux_child_follow_fork): If we're staying attached
+ to the child process, enable event reporting on it. Don't handle
+ checkpoints here. Instead, add the child fork to the lwp thread
+ and inferior lists without clobbering the previous inferior. Let
+ the thread_db layer learn about a new child process, even if
+ following the parent.
+ (linux_nat_switch_fork): Delete lwps of the current inferior only,
+ instead of clearing the whole list. Use thread_change_ptid to
+ give the core the illusion the new checkpoint is still the same
+ inferior. Clear the register cache.
+ (linux_handle_extended_wait): Handle checkpoints here.
+ (linux_multi_process): Turn on.
+ * linux-fork.c (struct fork_info) <pc>: Remove field.
+ (init_fork_list): Do not delete the checkpoint from the inferior
+ list (it is not there).
+ (fork_load_infrun_state): Don't switch inferior_ptid here. Pass
+ the new checkpoint's ptid to linux_nat_switch_fork.
+ (fork_save_infrun_state): Make static. Don't stop the pc field of
+ fork_info, it's gone.
+ (linux_fork_mourn_inferior): Don't delete the checkpoint from the
+ inferior list, it's not there.
+ (linux_fork_detach): Ditto.
+ (delete_fork_command): Replace mention of fork/checkpoint by
+ checkpoint only.
+ (detach_fork_command): Likewise. Don't delete the checkpoint from
+ the inferior list.
+ (info_forks_command): Adjust.
+ (restore_detach_fork): Delete.
+ (checkpointing_pid): New.
+ (linux_fork_checkpointing_p): New.
+ (save_detach_fork): Delete.
+ (checkpoint_command): Delete temp_detach_fork. Don't remove
+ breakpoints, that's a nop. Store the pid of the process we're
+ checkpointing, and use make_cleanup_restore_integer to restore it.
+ Don't reinsert breakpoints here.
+ (process_command, fork_command): Delete.
+ (restart_command): Update comments to only mention checkpoints,
+ not forks.
+ (_initialize_linux_fork): Delete "fork", "process", "info forks"
+ commands.
+ * linux-fork.h (fork_save_infrun_state, fork_list): Delete
+ declarations.
+ (linux_fork_checkpointing_p): Declare.
+ * cli/cli-cmds.c (killlist): New.
+ * cli/cli-cmds.h (killlist): Declare.
+ * gdbcmd.h (killlist): Declare.
+ * inferior.c: Include "gdbthread.h".
+ (detach_inferior_command, kill_inferior_command)
+ (inferior_command): New.
+ (info_inferiors_command): Allow specifying a specific inferior id.
+ (_initialize_inferiors): Register "inferior", "kill inferior" and
+ "detach inferior" commands.
+ * infcmd.c (_initialize_infcmd): Make "kill" a prefix command.
+ * gdbthread.h (any_thread_of_process): Declare.
+ * thread.c (any_thread_of_process): New.
+
+ * NEWS: Mention multi-inferior debugging. Mention 'info
+ inferiors', 'inferior', 'detach inferior' and 'kill inferior' as
+ new commands.
+ (Removed commands): New section, mentioning that 'info forks',
+ 'fork', 'process', 'delete fork' and 'detach fork' are now gone.
+
2009-07-02 Ulrich Weigand <uweigand@de.ibm.com>
* gdbarch.sh (current_gdbarch): Remove global variable.
* Support for user-defined prefixed commands. The "define" command can
add new commands to existing prefixes, e.g. "target".
+* Multi-inferior, multi-process debugging.
+
+ GDB now has generalized support for multi-inferior debugging. See
+ "Debugging Multiple Inferiors" in the manual for more information.
+ Although availability still depends on target support, the command
+ set is more uniform now. The GNU/Linux specific multi-forks support
+ has been migrated to this new framework. This implied some user
+ visible changes; see "New commands" and also "Removed commands"
+ below.
+
* New commands (for set/show, see "New options" below)
find [/size-char] [/max-count] start-address, end-address|+search-space-size,
info os processes
Show operating system information about processes.
+info inferiors
+ List the inferiors currently under GDB's control.
+
+inferior NUM
+ Switch focus to inferior number NUM.
+
+detach inferior NUM
+ Detach from inferior number NUM.
+
+kill inferior NUM
+ Kill inferior number NUM.
+
* New options
set sh calling-convention
Allow GDB to resume all threads of all processes or only threads of
the current process.
+* Removed commands
+
+info forks
+ For program forks, this is replaced by the new more generic `info
+ inferiors' command. To list checkpoints, you can still use the
+ `info checkpoints' command, which was an alias for the `info forks'
+ command.
+
+fork NUM
+ Replaced by the new `inferior' command. To switch between
+ checkpoints, you can still use the `restart' command, which was an
+ alias for the `fork' command.
+
+process PID
+ This is removed, since some targets don't have a notion of
+ processes. To switch between processes, you can still use the
+ `inferior' command using GDB's own inferior number.
+
+delete fork NUM
+ For program forks, this is replaced by the new more generic `kill
+ inferior' command. To delete a checkpoint, you can still use the
+ `delete checkpoint' command, which was an alias for the `delete
+ fork' command.
+
+detach fork NUM
+ For program forks, this is replaced by the new more generic `detach
+ inferior' command. To detach a checkpoint, you can still use the
+ `detach checkpoint' command, which was an alias for the `detach
+ fork' command.
+
* New native configurations
x86/x86_64 Darwin i[34567]86-*-darwin*
struct cmd_list_element *detachlist;
+/* Chain containing all defined kill subcommands. */
+
+struct cmd_list_element *killlist;
+
/* Chain containing all defined "enable breakpoint" subcommands. */
struct cmd_list_element *enablebreaklist;
extern struct cmd_list_element *detachlist;
+/* Chain containing all defined kill subcommands. */
+
+extern struct cmd_list_element *killlist;
+
/* Chain containing all defined toggle subcommands. */
extern struct cmd_list_element *togglelist;
+2009-07-02 Pedro Alves <pedro@codesourcery.com>
+
+ * gdb.texinfo (Debugging multiple inferiors): Document the
+ "inferior", "detach inferior" and "kill inferior" commands.
+ (Debugging Programs with Multiple Processes): Adjust to mention
+ generic "inferior" commands. Delete mention of "detach fork" and
+ "delete fork". Cross reference to "Debugging multiple inferiors"
+ section.
+
2009-07-02 Ulrich Weigand <uweigand@de.ibm.com>
* gdbint.texinfo (Item Output Functions): Update signature
@kindex info inferiors
@item info inferiors
Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior. The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
+
+@table @code
@kindex set print inferior-events
@cindex print messages on inferior start and exit
@item set print inferior-events
Show whether detach-on-fork mode is on/off.
@end table
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks). You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process. The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process. The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @code{inferior} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list. The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command. @xref{Inferiors, ,Debugging Multiple Inferiors}.
If you ask to debug a child process and a @code{vfork} is followed by an
@code{exec}, @value{GDBN} executes the new target up to the first
@code{main} in your original program, the breakpoint will also be set on
the child process's @code{main}.
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
If you issue a @code{run} command to @value{GDBN} after an @code{exec}
call executes, the new target restarts. To restart the parent process,
extern struct cmd_list_element *detachlist;
+/* Chain containing all defined kill subcommands. */
+
+extern struct cmd_list_element *killlist;
+
/* Chain containing all defined toggle subcommands. */
extern struct cmd_list_element *togglelist;
returns the first thread in the list. */
struct thread_info *first_thread_of_process (int pid);
+/* Returns any thread of process PID. */
+extern struct thread_info *any_thread_of_process (int pid);
+
/* Change the ptid of thread OLD_PTID to NEW_PTID. */
void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
&showlist);
set_cmd_completer (c, noop_completer);
- add_com ("kill", class_run, kill_command,
- _("Kill execution of program being debugged."));
+ add_prefix_cmd ("kill", class_run, kill_command,
+ _("Kill execution of program being debugged."),
+ &killlist, "kill ", 0, &cmdlist);
add_com ("attach", class_run, attach_command, _("\
Attach to a process or file outside of GDB.\n\
#include "gdbthread.h"
#include "ui-out.h"
#include "observer.h"
+#include "gdbthread.h"
void _initialize_inferiors (void);
do_cleanups (old_chain);
}
+static void
+detach_inferior_command (char *args, int from_tty)
+{
+ int num, pid;
+ struct thread_info *tp;
+
+ if (!args || !*args)
+ error (_("Requires argument (inferior id to detach)"));
+
+ num = parse_and_eval_long (args);
+
+ if (!valid_gdb_inferior_id (num))
+ error (_("Inferior ID %d not known."), num);
+
+ pid = gdb_inferior_id_to_pid (num);
+
+ tp = any_thread_of_process (pid);
+ if (!tp)
+ error (_("Inferior has no threads."));
+
+ switch_to_thread (tp->ptid);
+
+ detach_command (NULL, from_tty);
+}
+
+static void
+kill_inferior_command (char *args, int from_tty)
+{
+ int num, pid;
+ struct thread_info *tp;
+
+ if (!args || !*args)
+ error (_("Requires argument (inferior id to kill)"));
+
+ num = parse_and_eval_long (args);
+
+ if (!valid_gdb_inferior_id (num))
+ error (_("Inferior ID %d not known."), num);
+
+ pid = gdb_inferior_id_to_pid (num);
+
+ tp = any_thread_of_process (pid);
+ if (!tp)
+ error (_("Inferior has no threads."));
+
+ switch_to_thread (tp->ptid);
+
+ target_kill ();
+
+ bfd_cache_close_all ();
+}
+
+static void
+inferior_command (char *args, int from_tty)
+{
+ int num, pid;
+
+ if (!have_inferiors ())
+ error (_("No inferiors"));
+
+ num = parse_and_eval_long (args);
+
+ if (!valid_gdb_inferior_id (num))
+ error (_("Inferior ID %d not known."), num);
+
+ pid = gdb_inferior_id_to_pid (num);
+
+ if (pid != ptid_get_pid (inferior_ptid))
+ {
+ struct thread_info *tp;
+
+ tp = any_thread_of_process (pid);
+ if (!tp)
+ error (_("Inferior has no threads."));
+
+ switch_to_thread (tp->ptid);
+ }
+
+ printf_filtered (_("[Switching to thread %d (%s)] "),
+ pid_to_thread_id (inferior_ptid),
+ target_pid_to_str (inferior_ptid));
+
+ if (is_running (inferior_ptid))
+ ui_out_text (uiout, "(running)\n");
+ else
+ {
+ ui_out_text (uiout, "\n");
+ print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+ }
+}
+
/* Print information about currently known inferiors. */
static void
-info_inferiors_command (char *arg, int from_tty)
+info_inferiors_command (char *args, int from_tty)
{
- print_inferior (uiout, -1);
+ int requested = -1;
+
+ if (args && *args)
+ {
+ requested = parse_and_eval_long (args);
+ if (!valid_gdb_inferior_id (requested))
+ error (_("Inferior ID %d not known."), requested);
+ }
+
+ print_inferior (uiout, requested);
}
/* Print notices when new inferiors are created and die. */
NULL,
show_print_inferior_events,
&setprintlist, &showprintlist);
+
+ add_cmd ("inferior", class_run, detach_inferior_command, _("\
+Detach from inferior ID."),
+ &detachlist);
+
+ add_cmd ("inferior", class_run, kill_inferior_command, _("\
+Kill inferior ID."),
+ &killlist);
+
+ add_cmd ("inferior", class_run, inferior_command, _("\
+Use this command to switch between inferiors.\n\
+The new inferior ID must be currently known."),
+ &cmdlist);
}
struct regcache *savedregs; /* Convenient for info fork, saves
having to actually switch contexts. */
int clobber_regs; /* True if we should restore saved regs. */
- ULONGEST pc; /* PC for info fork. */
off_t *filepos; /* Set of open file descriptors' offsets. */
int maxfd;
};
for (fp = fork_list; fp; fp = fpnext)
{
fpnext = fp->next;
- delete_inferior (ptid_get_pid (fp->ptid));
free_fork (fp);
}
extern void nullify_last_target_wait_ptid ();
int i;
- inferior_ptid = fp->ptid;
-
- linux_nat_switch_fork (inferior_ptid);
+ linux_nat_switch_fork (fp->ptid);
if (fp->savedregs && fp->clobber_regs)
regcache_cpy (get_current_regcache (), fp->savedregs);
/* Save infrun state for the fork PTID.
Exported for use by linux child_follow_fork. */
-extern void
+static void
fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
{
char path[MAXPATHLEN];
fp->savedregs = regcache_dup (get_current_regcache ());
fp->clobber_regs = clobber_regs;
- fp->pc = regcache_read_pc (get_current_regcache ());
if (clobber_regs)
{
We need to delete that one from the fork_list, and switch
to the next available fork. */
delete_fork (inferior_ptid);
- /* Delete process from GDB's inferior list. */
- delete_inferior (ptid_get_pid (inferior_ptid));
/* There should still be a fork - if there's only one left,
delete_fork won't remove it, because we haven't updated
error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
delete_fork (inferior_ptid);
- /* Delete process from GDB's inferior list. */
- delete_inferior (ptid_get_pid (inferior_ptid));
/* There should still be a fork - if there's only one left,
delete_fork won't remove it, because we haven't updated
ptid_t ptid;
if (!args || !*args)
- error (_("Requires argument (fork/checkpoint id to delete)"));
+ error (_("Requires argument (checkpoint id to delete)"));
ptid = fork_id_to_ptid (parse_and_eval_long (args));
if (ptid_equal (ptid, minus_one_ptid))
- error (_("No such fork/checkpoint id, %s"), args);
+ error (_("No such checkpoint id, %s"), args);
if (ptid_equal (ptid, inferior_ptid))
- error (_("Please switch to another fork/checkpoint before deleting the current one"));
+ error (_("Please switch to another checkpoint before deleting the current one"));
if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
delete_fork (ptid);
- /* Delete process from GDB's inferior list. */
- delete_inferior (ptid_get_pid (ptid));
}
static void
ptid_t ptid;
if (!args || !*args)
- error (_("Requires argument (fork id to detach)"));
+ error (_("Requires argument (checkpoint id to detach)"));
ptid = fork_id_to_ptid (parse_and_eval_long (args));
if (ptid_equal (ptid, minus_one_ptid))
- error (_("No such fork id, %s"), args);
+ error (_("No such checkpoint id, %s"), args);
if (ptid_equal (ptid, inferior_ptid))
- error (_("Please switch to another fork before detaching the current one"));
+ error (_("\
+Please switch to another checkpoint before detaching the current one"));
if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
error (_("Unable to detach %s"), target_pid_to_str (ptid));
printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
delete_fork (ptid);
- /* Delete process from GDB's process table. */
- detach_inferior (ptid_get_pid (ptid));
}
/* Print information about currently known forks. */
else
{
printf_filtered (" ");
- pc = fp->pc;
+ pc = regcache_read_pc (fp->savedregs);
}
printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
if (fp->num == 0)
if (printed == NULL)
{
if (requested > 0)
- printf_filtered (_("No fork number %d.\n"), requested);
+ printf_filtered (_("No checkpoint number %d.\n"), requested);
else
- printf_filtered (_("No forks.\n"));
+ printf_filtered (_("No checkpoints.\n"));
}
}
-/* Save/restore mode variable 'detach_fork':
- We need to temporarily take over this mode variable, while
- preserving the user-specified state, and make sure that it
- gets restored in case of error.
-
- The int pointer that we use comes from the caller, so we can
- be called more than once (even though currently we don't need to). */
-
-static void
-restore_detach_fork (void *arg)
-{
- detach_fork = *(int *) arg;
-}
+/* The PID of the process we're checkpointing. */
+static int checkpointing_pid = 0;
-static struct cleanup *
-save_detach_fork (int *saved_val)
+int
+linux_fork_checkpointing_p (int pid)
{
- *saved_val = detach_fork;
- return make_cleanup (restore_detach_fork, (void *) saved_val);
+ return (checkpointing_pid == pid);
}
static void
pid_t retpid;
struct cleanup *old_chain;
long i;
- /* Make this temp var static, 'cause it's used in the error context. */
- static int temp_detach_fork;
-
- /* Remove breakpoints, so that they are not inserted
- in the forked process. */
- remove_breakpoints ();
/* Make the inferior fork, record its (and gdb's) state. */
gdbarch = get_objfile_arch (fork_objf);
ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
- old_chain = save_detach_fork (&temp_detach_fork);
- detach_fork = 0;
+
+ /* Tell linux-nat.c that we're checkpointing this inferior. */
+ old_chain = make_cleanup_restore_integer (&checkpointing_pid);
+ checkpointing_pid = PIDGET (inferior_ptid);
+
ret = call_function_by_hand (fork_fn, 0, &ret);
do_cleanups (old_chain);
if (!ret) /* Probably can't happen. */
if (!fp)
error (_("Failed to find new fork"));
fork_save_infrun_state (fp, 1);
- insert_breakpoints ();
}
static void
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
}
-/* Switch inferior process (fork) context, by process id. */
-static void
-process_command (char *args, int from_tty)
-{
- struct fork_info *fp;
-
- if (!args || !*args)
- error (_("Requires argument (process id to switch to)"));
-
- if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
- error (_("Not found: process id %s"), args);
-
- linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by fork id. */
-static void
-fork_command (char *args, int from_tty)
-{
- struct fork_info *fp;
-
- if (!args || !*args)
- error (_("Requires argument (fork id to switch to)"));
-
- if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
- error (_("Not found: fork id %s"), args);
-
- linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by checkpoint id. */
+/* Switch inferior process (checkpoint) context, by checkpoint id. */
static void
restart_command (char *args, int from_tty)
{
add_com ("checkpoint", class_obscure, checkpoint_command, _("\
Fork a duplicate process (experimental)."));
- /* Restart command: restore the context of a specified fork
- process. May be used for "program forks" as well as for
- "debugger forks" (checkpoints). */
+ /* Restart command: restore the context of a specified checkpoint
+ process. */
add_com ("restart", class_obscure, restart_command, _("\
restart <n>: restore program context from a checkpoint.\n\
fork list. */
add_cmd ("checkpoint", class_obscure, delete_fork_command, _("\
-Delete a fork/checkpoint (experimental)."),
+Delete a checkpoint (experimental)."),
&deletelist);
/* Detach checkpoint command: release the process to run independently,
and remove it from the fork list. */
add_cmd ("checkpoint", class_obscure, detach_fork_command, _("\
-Detach from a fork/checkpoint (experimental)."),
+Detach from a checkpoint (experimental)."),
&detachlist);
/* Info checkpoints command: list all forks/checkpoints
currently under gdb's control. */
add_info ("checkpoints", info_forks_command,
- _("IDs of currently known forks/checkpoints."));
-
- /* Command aliases (let "fork" and "checkpoint" be used
- interchangeably). */
-
- add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
- add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
- add_info_alias ("forks", "checkpoints", 0);
-
- /* "fork <n>" (by analogy to "thread <n>"). */
- add_com ("fork", class_obscure, fork_command, _("\
-fork <n>: Switch between forked processes.\n\
-Argument 'n' is fork ID, as displayed by 'info forks'."));
-
- /* "process <proc id>" as opposed to "fork <fork id>". */
- add_com ("process", class_obscure, process_command, _("\
-process <pid>: Switch between forked processes.\n\
-Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
+ _("IDs of currently known checkpoints."));
}
struct fork_info;
extern struct fork_info *add_fork (pid_t);
extern struct fork_info *find_fork_pid (pid_t);
-extern void fork_save_infrun_state (struct fork_info *, int);
extern void linux_fork_killall (void);
extern void linux_fork_mourn_inferior (void);
extern void linux_fork_detach (char *, int);
-extern int forks_exist_p (void);
-
-struct fork_info *fork_list;
+extern int forks_exist_p (void);
+extern int linux_fork_checkpointing_p (int);
extern int detach_fork;
static void block_child_signals (sigset_t *prev_mask);
static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
\f
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
parent_pid = ptid_get_pid (inferior_ptid);
child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
+ if (!detach_fork)
+ linux_enable_event_reporting (pid_to_ptid (child_pid));
+
if (! follow_child)
{
/* We're already attached to the parent, by default. */
}
else
{
- struct fork_info *fp;
struct inferior *parent_inf, *child_inf;
+ struct lwp_info *lp;
+ struct cleanup *old_chain;
/* Add process to GDB's tables. */
child_inf = add_inferior (child_pid);
child_inf->attach_flag = parent_inf->attach_flag;
copy_terminal_info (child_inf, parent_inf);
- /* Retain child fork in ptrace (stopped) state. */
- fp = find_fork_pid (child_pid);
- if (!fp)
- fp = add_fork (child_pid);
- fork_save_infrun_state (fp, 0);
+ old_chain = save_inferior_ptid ();
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ lp = add_lwp (inferior_ptid);
+ lp->stopped = 1;
+
+ check_for_thread_db ();
+
+ do_cleanups (old_chain);
}
if (has_vforked)
{
struct thread_info *tp;
struct inferior *parent_inf, *child_inf;
+ struct lwp_info *lp;
/* Before detaching from the parent, remove all breakpoints from it. */
remove_breakpoints ();
if (has_vforked)
{
+ struct lwp_info *parent_lwp;
+
linux_parent_pid = parent_pid;
+
+ /* Get rid of the inferior on the core side as well. */
+ inferior_ptid = null_ptid;
detach_inferior (parent_pid);
- }
- else if (!detach_fork)
- {
- struct fork_info *fp;
- /* Retain parent fork in ptrace (stopped) state. */
- fp = find_fork_pid (parent_pid);
- if (!fp)
- fp = add_fork (parent_pid);
- fork_save_infrun_state (fp, 0);
- /* Also add an entry for the child fork. */
- fp = find_fork_pid (child_pid);
- if (!fp)
- fp = add_fork (child_pid);
- fork_save_infrun_state (fp, 0);
+ /* Also get rid of all its lwps. We will detach from this
+ inferior soon-ish, but, we will still get an exit event
+ reported through waitpid when it exits. If we didn't get
+ rid of the lwps from our list, we would end up reporting
+ the inferior exit to the core, which would then try to
+ mourn a non-existing (from the core's perspective)
+ inferior. */
+ parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+ purge_lwp_list (GET_PID (parent_lwp->ptid));
+ linux_parent_pid = parent_pid;
}
- else
+ else if (detach_fork)
target_detach (NULL, 0);
inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ lp = add_lwp (inferior_ptid);
+ lp->stopped = 1;
- linux_nat_switch_fork (inferior_ptid);
check_for_thread_db ();
}
return NULL;
}
-/* Update our internal state when changing from one fork (checkpoint,
- et cetera) to another indicated by NEW_PTID. We can only switch
- single-threaded applications, so we only create one new LWP, and
- the previous list is discarded. */
+/* Update our internal state when changing from one checkpoint to
+ another indicated by NEW_PTID. We can only switch single-threaded
+ applications, so we only create one new LWP, and the previous list
+ is discarded. */
void
linux_nat_switch_fork (ptid_t new_ptid)
{
struct lwp_info *lp;
- init_lwp_list ();
+ purge_lwp_list (GET_PID (inferior_ptid));
+
lp = add_lwp (new_ptid);
lp->stopped = 1;
- init_thread_list ();
- add_thread_silent (new_ptid);
+ /* This changes the thread's ptid while preserving the gdb thread
+ num. Also changes the inferior pid, while preserving the
+ inferior num. */
+ thread_change_ptid (inferior_ptid, new_ptid);
+
+ /* We've just told GDB core that the thread changed target id, but,
+ in fact, it really is a different thread, with different register
+ contents. */
+ registers_changed ();
}
/* Handle the exit of a single thread LP. */
ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
+ if (event == PTRACE_EVENT_FORK
+ && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+ {
+ struct fork_info *fp;
+
+ /* Handle checkpointing by linux-fork.c here as a special
+ case. We don't want the follow-fork-mode or 'catch fork'
+ to interfere with this. */
+
+ /* This won't actually modify the breakpoint list, but will
+ physically remove the breakpoints from the child. */
+ detach_breakpoints (new_pid);
+
+ /* Retain child fork in ptrace (stopped) state. */
+ fp = find_fork_pid (new_pid);
+ if (!fp)
+ fp = add_fork (new_pid);
+
+ /* Report as spurious, so that infrun doesn't want to follow
+ this fork. We're actually doing an infcall in
+ linux-fork.c. */
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+ /* Report the stop to the core. */
+ return 0;
+ }
+
if (event == PTRACE_EVENT_FORK)
ourstatus->kind = TARGET_WAITKIND_FORKED;
else if (event == PTRACE_EVENT_VFORK)
/* True if we want to support multi-process. To be removed when GDB
supports multi-exec. */
-int linux_multi_process = 0;
+int linux_multi_process = 1;
static int
linux_nat_supports_multi_process (void)
+2009-07-02 Pedro Alves <pedro@codesourcery.com>
+
+ * gdb.base/multi-forks.exp: Only run detach-on-fork tests on
+ linux. Adjust to use "inferior", "info inferiors", "detach
+ inferior" and "kill inferior" instead of "restart", "info fork",
+ "detach fork" and "delete fork".
+ * gdb.base/ending-run.exp: Spell out "info".
+ * gdb.base/help.exp: Adjust to use test_prefix_command_help for
+ the "kill" command.
+
2009-07-02 Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
* gdb.threads/tls-shared.exp: Update to locexpr_describe_location
gdb_test "cle ending-run.c:14" \
".*Deleted breakpoint 5.*" "Cleared 2 by line"
-send_gdb "inf line ending-run.c:14\n"
+send_gdb "info line ending-run.c:14\n"
gdb_expect {
-re ".*address (0x\[0-9a-fA-F]*).*$gdb_prompt $" {
set line_nine $expect_out(1,string)
# test help jump
gdb_test "help jump" "Continue program being debugged at specified line or address\.\[\r\n\]+Give as argument either LINENUM or \[*\]+ADDR, where ADDR is an expression\[\r\n\]+for an address to start at\." "help jump"
# test help kill
-gdb_test "help kill" "Kill execution of program being debugged\." "help kill"
+test_prefix_command_help "kill" {
+ "Kill execution of program being debugged\.\[\r\n\]+"
+}
# test help list "l" abbreviation
gdb_test "help l" "List specified function or line\.\[\r\n\]+With no argument, lists ten more lines after or around previous listing\.\[\r\n\]+\"list -\" lists the ten lines before a previous ten-line listing\.\[\r\n\]+One argument specifies a line, and ten lines are listed around that line\.\[\r\n\]+Two arguments with comma between specify starting and ending lines to list\.\[\r\n\]+Lines can be specified in these ways:\[\r\n\]+ LINENUM, to list around that line in current file,\[\r\n\]+ FILE:LINENUM, to list around that line in that file,\[\r\n\]+ FUNCTION, to list around beginning of that function,\[\r\n\]+ FILE:FUNCTION, to distinguish among like-named static functions\.\[\r\n\]+ \[*\]ADDRESS, to list around the line containing that address\.\[\r\n\]+With two args if one is empty it stands for ten lines away from the other arg\." "help list \"l\" abbreviation"
# test help list
# Now test with detach-on-fork off.
#
+# detach-on-fork isn't implemented on hpux.
+#
+if {![istarget "*-*-linux*"]} then {
+ continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
runto_main
gdb_breakpoint $exit_bp_loc
#
# We will now run every fork up to the exit bp,
-# eventually winding up with 16 forks.
+# eventually winding up with 16 inferiors.
#
for {set i 1} {$i <= 15} {incr i} {
gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit $i"
- gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork $i"
- gdb_test "restart $i" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
- "restart $i"
+ gdb_test "info inferior" " 5 .* 4 .* 3 .* 2 .*" "info inferior $i"
+ gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
+ "inferior $i"
}
gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit 16"
-gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork 16"
-gdb_test "restart 0" " main .*" "restart final"
+gdb_test "info inferiors" " 5 .* 4 .* 3 .* 2 .*" "info inferior 16"
+gdb_test "inferior 2" " main .*" "restart final"
#
# Now we should examine all the pids.
#
#
-# Test detach fork
+# Test detach inferior
#
-# [assumes we're at #0]
-gdb_test "detach fork 1" "Detached .*" "Detach 1"
-gdb_test "detach fork 2" "Detached .*" "Detach 2"
-gdb_test "detach fork 3" "Detached .*" "Detach 3"
-gdb_test "detach fork 4" "Detached .*" "Detach 4"
+# [assumes we're at #1]
+gdb_test "detach inferior 2" "Detaching .*" "Detach 2"
+gdb_test "detach inferior 3" "Detaching .*" "Detach 3"
+gdb_test "detach inferior 4" "Detaching .*" "Detach 4"
+gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
#
-# Test delete fork
-#
+# Test kill inferior
+#
-gdb_test "delete fork 5" "" "Delete 5"
-gdb_test "info fork 5" "No fork number 5." "Did delete 5"
-gdb_test "delete fork 6" "" "Delete 6"
-gdb_test "info fork 6" "No fork number 6." "Did delete 6"
-gdb_test "delete fork 7" "" "Delete 7"
-gdb_test "info fork 7" "No fork number 7." "Did delete 7"
-gdb_test "delete fork 8" "" "Delete 8"
-gdb_test "info fork 8" "No fork number 8." "Did delete 8"
-gdb_test "delete fork 9" "" "Delete 9"
-gdb_test "info fork 9" "No fork number 9." "Did delete 9"
-gdb_test "delete fork 10" "" "Delete 10"
-gdb_test "info fork 10" "No fork number 10." "Did delete 10"
-gdb_test "delete fork 11" "" "Delete 11"
-gdb_test "info fork 11" "No fork number 11." "Did delete 11"
-gdb_test "delete fork 12" "" "Delete 12"
-gdb_test "info fork 12" "No fork number 12." "Did delete 12"
-gdb_test "delete fork 13" "" "Delete 13"
-gdb_test "info fork 13" "No fork number 13." "Did delete 13"
-gdb_test "delete fork 14" "" "Delete 14"
-gdb_test "info fork 14" "No fork number 14." "Did delete 14"
-gdb_test "delete fork 15" "" "Delete 15"
-gdb_test "info fork 15" "No fork number 15." "Did delete 15"
+gdb_test "kill inferior 6" "" "Kill 6"
+gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
+gdb_test "kill inferior 7" "" "Kill 7"
+gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
+gdb_test "kill inferior 8" "" "Kill 8"
+gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
+gdb_test "kill inferior 9" "" "Kill 9"
+gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
+gdb_test "kill inferior 10" "" "Kill 10"
+gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
+gdb_test "kill inferior 11" "" "Kill 11"
+gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
+gdb_test "kill inferior 12" "" "Kill 12"
+gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
+gdb_test "kill inferior 13" "" "Kill 13"
+gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
+gdb_test "kill inferior 14" "" "Kill 14"
+gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
+gdb_test "kill inferior 15" "" "Kill 15"
+gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
+gdb_test "kill inferior 16" "" "Kill 16"
+gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
return 0
-
return ret;
}
+struct thread_info *
+any_thread_of_process (int pid)
+{
+ struct thread_info *tp;
+
+ for (tp = thread_list; tp; tp = tp->next)
+ if (ptid_get_pid (tp->ptid) == pid)
+ return tp;
+
+ return NULL;
+}
+
/* Print a list of thread ids currently known, and the total number of
threads. To be used from within catch_errors. */
static int