diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 155574db46a..020e21f37ec 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,67 @@ +2009-07-02 Pedro Alves + + * 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) : 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 * gdbarch.sh (current_gdbarch): Remove global variable. diff --git a/gdb/NEWS b/gdb/NEWS index b12c5dc874f..68776a58ff5 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -175,6 +175,16 @@ GDB will now correctly handle all of: * 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, @@ -197,6 +207,18 @@ macro undef 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 @@ -320,6 +342,36 @@ show schedule-multiple 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* diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 37b027ba68a..1b177512876 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -125,6 +125,10 @@ struct cmd_list_element *deletelist; 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; diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h index 5b089361af7..3260fe8d748 100644 --- a/gdb/cli/cli-cmds.h +++ b/gdb/cli/cli-cmds.h @@ -41,6 +41,10 @@ extern struct cmd_list_element *deletelist; 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; diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index f5d4950f49b..2ed8cc8d041 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,12 @@ +2009-07-02 Pedro Alves + + * 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 * gdbint.texinfo (Item Output Functions): Update signature diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 272e80749a3..d1ce3e133a0 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2374,7 +2374,39 @@ To find out what inferiors exist at any moment, use @code{info inferiors}: @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 @@ -2758,51 +2790,17 @@ is held suspended. 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 @@ -2810,8 +2808,8 @@ breakpoint in the new target. If you have a breakpoint set on @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, diff --git a/gdb/gdbcmd.h b/gdb/gdbcmd.h index 57a06ede52f..0075fc1cc19 100644 --- a/gdb/gdbcmd.h +++ b/gdb/gdbcmd.h @@ -52,6 +52,10 @@ extern struct cmd_list_element *deletelist; 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; diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 956ef9a86a9..79d33fec0eb 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -243,6 +243,9 @@ struct thread_info *find_thread_id (int num); 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); diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 28b618f4f4a..9e98290708e 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2698,8 +2698,9 @@ fully linked executable files and separately compiled object files as needed."), &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\ diff --git a/gdb/inferior.c b/gdb/inferior.c index a99a3be1671..43eacda4463 100644 --- a/gdb/inferior.c +++ b/gdb/inferior.c @@ -25,6 +25,7 @@ #include "gdbthread.h" #include "ui-out.h" #include "observer.h" +#include "gdbthread.h" void _initialize_inferiors (void); @@ -352,12 +353,112 @@ print_inferior (struct ui_out *uiout, int requested_inferior) 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. */ @@ -381,4 +482,17 @@ Show printing of inferior events (e.g., inferior start and exit)."), NULL, 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); } diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c index b89e8b52981..c0004d16b27 100644 --- a/gdb/linux-fork.c +++ b/gdb/linux-fork.c @@ -53,7 +53,6 @@ struct fork_info 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; }; @@ -211,7 +210,6 @@ init_fork_list (void) for (fp = fork_list; fp; fp = fpnext) { fpnext = fp->next; - delete_inferior (ptid_get_pid (fp->ptid)); free_fork (fp); } @@ -241,9 +239,7 @@ fork_load_infrun_state (struct fork_info *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); @@ -269,7 +265,7 @@ fork_load_infrun_state (struct fork_info *fp) /* 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]; @@ -281,7 +277,6 @@ fork_save_infrun_state (struct fork_info *fp, int clobber_regs) fp->savedregs = regcache_dup (get_current_regcache ()); fp->clobber_regs = clobber_regs; - fp->pc = regcache_read_pc (get_current_regcache ()); if (clobber_regs) { @@ -371,8 +366,6 @@ linux_fork_mourn_inferior (void) 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 @@ -403,8 +396,6 @@ linux_fork_detach (char *args, int from_tty) 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 @@ -430,14 +421,14 @@ delete_fork_command (char *args, int from_tty) 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)); @@ -446,8 +437,6 @@ delete_fork_command (char *args, int from_tty) 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 @@ -456,14 +445,15 @@ detach_fork_command (char *args, int from_tty) 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)); @@ -472,8 +462,6 @@ detach_fork_command (char *args, int from_tty) 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. */ @@ -508,7 +496,7 @@ info_forks_command (char *arg, int from_tty) 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) @@ -542,31 +530,19 @@ info_forks_command (char *arg, int from_tty) 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 PID of the process we're checkpointing. */ +static int checkpointing_pid = 0; - 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) +int +linux_fork_checkpointing_p (int pid) { - detach_fork = *(int *) arg; -} - -static struct cleanup * -save_detach_fork (int *saved_val) -{ - *saved_val = detach_fork; - return make_cleanup (restore_detach_fork, (void *) saved_val); + return (checkpointing_pid == pid); } static void @@ -581,12 +557,6 @@ checkpoint_command (char *args, int from_tty) 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. */ @@ -600,8 +570,11 @@ checkpoint_command (char *args, int from_tty) 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. */ @@ -629,7 +602,6 @@ checkpoint_command (char *args, int from_tty) if (!fp) error (_("Failed to find new fork")); fork_save_infrun_state (fp, 1); - insert_breakpoints (); } static void @@ -656,37 +628,7 @@ linux_fork_context (struct fork_info *newfp, int from_tty) 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) { @@ -727,9 +669,8 @@ Tells gdb whether to detach the child of a fork."), 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 : restore program context from a checkpoint.\n\ @@ -739,36 +680,19 @@ Argument 'n' is checkpoint ID, as displayed by 'info checkpoints'.")); 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 " (by analogy to "thread "). */ - add_com ("fork", class_obscure, fork_command, _("\ -fork : Switch between forked processes.\n\ -Argument 'n' is fork ID, as displayed by 'info forks'.")); - - /* "process " as opposed to "fork ". */ - add_com ("process", class_obscure, process_command, _("\ -process : Switch between forked processes.\n\ -Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'.")); + _("IDs of currently known checkpoints.")); } diff --git a/gdb/linux-fork.h b/gdb/linux-fork.h index 0e7d1b6b3aa..23e99199efc 100644 --- a/gdb/linux-fork.h +++ b/gdb/linux-fork.h @@ -20,13 +20,11 @@ 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; diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 0253fe91d6d..f8d7a5c1114 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -338,6 +338,12 @@ static int stop_callback (struct lwp_info *lp, void *data); 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); + /* Trivial list manipulation functions to keep track of a list of new stopped processes. */ @@ -587,6 +593,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child) 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. */ @@ -617,8 +626,9 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child) } 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); @@ -627,11 +637,16 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child) 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) @@ -692,6 +707,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child) { 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 (); @@ -733,30 +749,33 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child) if (has_vforked) { - linux_parent_pid = parent_pid; - 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); + struct lwp_info *parent_lwp; - /* 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); + linux_parent_pid = parent_pid; + + /* Get rid of the inferior on the core side as well. */ + inferior_ptid = null_ptid; + detach_inferior (parent_pid); + + /* 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 (); } @@ -1073,22 +1092,30 @@ iterate_over_lwps (ptid_t filter, 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. */ @@ -1815,6 +1842,34 @@ linux_handle_extended_wait (struct lwp_info *lp, int status, 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) @@ -4295,7 +4350,7 @@ linux_nat_supports_non_stop (void) /* 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) diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ca40ca37fdd..2d28df15abe 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2009-07-02 Pedro Alves + + * 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 * gdb.threads/tls-shared.exp: Update to locexpr_describe_location diff --git a/gdb/testsuite/gdb.base/ending-run.exp b/gdb/testsuite/gdb.base/ending-run.exp index cf336023bef..bc97e373157 100644 --- a/gdb/testsuite/gdb.base/ending-run.exp +++ b/gdb/testsuite/gdb.base/ending-run.exp @@ -71,7 +71,7 @@ gdb_test "b ending-run.c:14" ".*Note.*also.*Breakpoint.*5.*" "b ending-run.c:14, 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) diff --git a/gdb/testsuite/gdb.base/help.exp b/gdb/testsuite/gdb.base/help.exp index 4618a2c6264..5647101f182 100644 --- a/gdb/testsuite/gdb.base/help.exp +++ b/gdb/testsuite/gdb.base/help.exp @@ -300,7 +300,9 @@ gdb_test "help inspect" "Same as \"print\" command, except that if you are runni # 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 diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp index f8531800cd4..c09c5590cf7 100644 --- a/gdb/testsuite/gdb.base/multi-forks.exp +++ b/gdb/testsuite/gdb.base/multi-forks.exp @@ -140,6 +140,19 @@ gdb_test "print pids\[0\]==0 || pids\[1\]==0 || pids\[2\]==0 || pids\[3\]==0" \ # 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 @@ -152,60 +165,59 @@ gdb_test "set detach off" "" "set detach off" # # 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 - diff --git a/gdb/thread.c b/gdb/thread.c index 536177130c6..a7ac3c8a9ae 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -428,6 +428,18 @@ first_thread_of_process (int pid) 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