gdb/linux-fork.c - gdb
Global variables defined
Data types defined
Functions defined
Source code
- #include "defs.h"
- #include "arch-utils.h"
- #include "inferior.h"
- #include "infrun.h"
- #include "regcache.h"
- #include "gdbcmd.h"
- #include "infcall.h"
- #include "objfiles.h"
- #include "linux-fork.h"
- #include "linux-nat.h"
- #include "gdbthread.h"
- #include "source.h"
- #include <sys/ptrace.h>
- #include "gdb_wait.h"
- #include <dirent.h>
- #include <ctype.h>
- struct fork_info *fork_list;
- static int highest_fork_num;
- extern void _initialize_linux_fork (void);
- struct fork_info
- {
- struct fork_info *next;
- ptid_t ptid;
- ptid_t parent_ptid;
- int num;
- struct regcache *savedregs;
- int clobber_regs;
- off_t *filepos;
- int maxfd;
- };
- int
- forks_exist_p (void)
- {
- return (fork_list != NULL);
- }
- struct fork_info *
- add_fork (pid_t pid)
- {
- struct fork_info *fp;
- if (fork_list == NULL && pid != ptid_get_pid (inferior_ptid))
- {
-
- highest_fork_num = -1;
- add_fork (ptid_get_pid (inferior_ptid));
- }
- fp = XCNEW (struct fork_info);
- fp->ptid = ptid_build (pid, pid, 0);
- fp->num = ++highest_fork_num;
- fp->next = fork_list;
- fork_list = fp;
- return fp;
- }
- static void
- free_fork (struct fork_info *fp)
- {
-
- if (fp)
- {
- if (fp->savedregs)
- regcache_xfree (fp->savedregs);
- if (fp->filepos)
- xfree (fp->filepos);
- xfree (fp);
- }
- }
- static void
- delete_fork (ptid_t ptid)
- {
- struct fork_info *fp, *fpprev;
- fpprev = NULL;
- linux_nat_forget_process (ptid_get_pid (ptid));
- for (fp = fork_list; fp; fpprev = fp, fp = fp->next)
- if (ptid_equal (fp->ptid, ptid))
- break;
- if (!fp)
- return;
- if (fpprev)
- fpprev->next = fp->next;
- else
- fork_list = fp->next;
- free_fork (fp);
-
- if (fork_list != NULL && fork_list->next == NULL &&
- ptid_equal (fork_list->ptid, inferior_ptid))
- {
-
- delete_fork (inferior_ptid);
- }
- }
- static struct fork_info *
- find_fork_ptid (ptid_t ptid)
- {
- struct fork_info *fp;
- for (fp = fork_list; fp; fp = fp->next)
- if (ptid_equal (fp->ptid, ptid))
- return fp;
- return NULL;
- }
- static struct fork_info *
- find_fork_id (int num)
- {
- struct fork_info *fp;
- for (fp = fork_list; fp; fp = fp->next)
- if (fp->num == num)
- return fp;
- return NULL;
- }
- extern struct fork_info *
- find_fork_pid (pid_t pid)
- {
- struct fork_info *fp;
- for (fp = fork_list; fp; fp = fp->next)
- if (pid == ptid_get_pid (fp->ptid))
- return fp;
- return NULL;
- }
- static ptid_t
- fork_id_to_ptid (int num)
- {
- struct fork_info *fork = find_fork_id (num);
- if (fork)
- return fork->ptid;
- else
- return pid_to_ptid (-1);
- }
- static void
- init_fork_list (void)
- {
- struct fork_info *fp, *fpnext;
- if (!fork_list)
- return;
- for (fp = fork_list; fp; fp = fpnext)
- {
- fpnext = fp->next;
- free_fork (fp);
- }
- fork_list = NULL;
- }
- static off_t
- call_lseek (int fd, off_t offset, int whence)
- {
- char exp[80];
- snprintf (&exp[0], sizeof (exp), "lseek (%d, %ld, %d)",
- fd, (long) offset, whence);
- return (off_t) parse_and_eval_long (&exp[0]);
- }
- static void
- fork_load_infrun_state (struct fork_info *fp)
- {
- extern void nullify_last_target_wait_ptid ();
- int i;
- linux_nat_switch_fork (fp->ptid);
- if (fp->savedregs && fp->clobber_regs)
- regcache_cpy (get_current_regcache (), fp->savedregs);
- registers_changed ();
- reinit_frame_cache ();
- stop_pc = regcache_read_pc (get_current_regcache ());
- nullify_last_target_wait_ptid ();
-
- if (fp->filepos)
- {
- for (i = 0; i <= fp->maxfd; i++)
- if (fp->filepos[i] != (off_t) -1)
- call_lseek (i, fp->filepos[i], SEEK_SET);
-
- }
- }
- static void
- fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
- {
- char path[PATH_MAX];
- struct dirent *de;
- DIR *d;
- if (fp->savedregs)
- regcache_xfree (fp->savedregs);
- fp->savedregs = regcache_dup (get_current_regcache ());
- fp->clobber_regs = clobber_regs;
- if (clobber_regs)
- {
-
- snprintf (path, PATH_MAX, "/proc/%ld/fd",
- (long) ptid_get_pid (fp->ptid));
- if ((d = opendir (path)) != NULL)
- {
- long tmp;
- fp->maxfd = 0;
- while ((de = readdir (d)) != NULL)
- {
-
- tmp = strtol (&de->d_name[0], NULL, 10);
- if (fp->maxfd < tmp)
- fp->maxfd = tmp;
- }
-
- fp->filepos = xrealloc (fp->filepos,
- (fp->maxfd + 1) * sizeof (*fp->filepos));
-
- for (tmp = 0; tmp <= fp->maxfd; tmp++)
- fp->filepos[tmp] = -1;
-
- rewinddir (d);
- while ((de = readdir (d)) != NULL)
- if (isdigit (de->d_name[0]))
- {
- tmp = strtol (&de->d_name[0], NULL, 10);
- fp->filepos[tmp] = call_lseek (tmp, 0, SEEK_CUR);
- }
- closedir (d);
- }
- }
- }
- void
- linux_fork_killall (void)
- {
-
- struct fork_info *fp;
- pid_t pid, ret;
- int status;
- for (fp = fork_list; fp; fp = fp->next)
- {
- pid = ptid_get_pid (fp->ptid);
- do {
-
- kill (pid, SIGKILL);
- ret = waitpid (pid, &status, 0);
-
- } while (ret == pid && WIFSTOPPED (status));
- }
- init_fork_list ();
- }
- void
- linux_fork_mourn_inferior (void)
- {
-
- int status;
- waitpid (ptid_get_pid (inferior_ptid), &status, 0);
-
- delete_fork (inferior_ptid);
-
- gdb_assert (fork_list);
- fork_load_infrun_state (fork_list);
- printf_filtered (_("[Switching to %s]\n"),
- target_pid_to_str (inferior_ptid));
-
- if (fork_list->next == NULL)
- delete_fork (inferior_ptid);
- }
- void
- linux_fork_detach (const char *args, int from_tty)
- {
-
- if (ptrace (PTRACE_DETACH, ptid_get_pid (inferior_ptid), 0, 0))
- error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
- delete_fork (inferior_ptid);
-
- gdb_assert (fork_list);
- fork_load_infrun_state (fork_list);
- if (from_tty)
- printf_filtered (_("[Switching to %s]\n"),
- target_pid_to_str (inferior_ptid));
-
- if (fork_list->next == NULL)
- delete_fork (inferior_ptid);
- }
- static void
- inferior_call_waitpid_cleanup (void *fp)
- {
- struct fork_info *oldfp = fp;
- if (oldfp)
- {
-
- remove_breakpoints ();
- fork_load_infrun_state (oldfp);
- insert_breakpoints ();
- }
- }
- static int
- inferior_call_waitpid (ptid_t pptid, int pid)
- {
- struct objfile *waitpid_objf;
- struct value *waitpid_fn = NULL;
- struct value *argv[4], *retv;
- struct gdbarch *gdbarch = get_current_arch ();
- struct fork_info *oldfp = NULL, *newfp = NULL;
- struct cleanup *old_cleanup;
- int ret = -1;
- if (!ptid_equal (pptid, inferior_ptid))
- {
-
- oldfp = find_fork_ptid (inferior_ptid);
- gdb_assert (oldfp != NULL);
- newfp = find_fork_ptid (pptid);
- gdb_assert (newfp != NULL);
- fork_save_infrun_state (oldfp, 1);
- remove_breakpoints ();
- fork_load_infrun_state (newfp);
- insert_breakpoints ();
- }
- old_cleanup = make_cleanup (inferior_call_waitpid_cleanup, oldfp);
-
- if (lookup_minimal_symbol ("waitpid", NULL, NULL).minsym != NULL)
- waitpid_fn = find_function_in_inferior ("waitpid", &waitpid_objf);
- if (!waitpid_fn
- && lookup_minimal_symbol ("_waitpid", NULL, NULL).minsym != NULL)
- waitpid_fn = find_function_in_inferior ("_waitpid", &waitpid_objf);
- if (!waitpid_fn)
- goto out;
-
- argv[0] = value_from_longest (builtin_type (gdbarch)->builtin_int, pid);
- argv[1] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr, 0);
- argv[2] = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
- argv[3] = 0;
- retv = call_function_by_hand (waitpid_fn, 3, argv);
- if (value_as_long (retv) < 0)
- goto out;
- ret = 0;
- out:
- do_cleanups (old_cleanup);
- return ret;
- }
- static void
- delete_checkpoint_command (char *args, int from_tty)
- {
- ptid_t ptid, pptid;
- struct fork_info *fi;
- if (!args || !*args)
- 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 checkpoint id, %s"), args);
- if (ptid_equal (ptid, inferior_ptid))
- error (_("\
- Please switch to another checkpoint before deleting the current one"));
- if (ptrace (PTRACE_KILL, ptid_get_pid (ptid), 0, 0))
- error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
- fi = find_fork_ptid (ptid);
- gdb_assert (fi);
- pptid = fi->parent_ptid;
- if (from_tty)
- printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
- delete_fork (ptid);
-
- if ((!find_thread_ptid (pptid) && find_fork_ptid (pptid))
- || (find_thread_ptid (pptid) && is_stopped (pptid)))
- {
- if (inferior_call_waitpid (pptid, ptid_get_pid (ptid)))
- warning (_("Unable to wait pid %s"), target_pid_to_str (ptid));
- }
- }
- static void
- detach_checkpoint_command (char *args, int from_tty)
- {
- ptid_t ptid;
- if (!args || !*args)
- 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 checkpoint id, %s"), args);
- if (ptid_equal (ptid, inferior_ptid))
- error (_("\
- Please switch to another checkpoint before detaching the current one"));
- if (ptrace (PTRACE_DETACH, ptid_get_pid (ptid), 0, 0))
- error (_("Unable to detach %s"), target_pid_to_str (ptid));
- if (from_tty)
- printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
- delete_fork (ptid);
- }
- static void
- info_checkpoints_command (char *arg, int from_tty)
- {
- struct gdbarch *gdbarch = get_current_arch ();
- struct symtab_and_line sal;
- struct fork_info *fp;
- ULONGEST pc;
- int requested = -1;
- struct fork_info *printed = NULL;
- if (arg && *arg)
- requested = (int) parse_and_eval_long (arg);
- for (fp = fork_list; fp; fp = fp->next)
- {
- if (requested > 0 && fp->num != requested)
- continue;
- printed = fp;
- if (ptid_equal (fp->ptid, inferior_ptid))
- {
- printf_filtered ("* ");
- pc = regcache_read_pc (get_current_regcache ());
- }
- else
- {
- printf_filtered (" ");
- pc = regcache_read_pc (fp->savedregs);
- }
- printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
- if (fp->num == 0)
- printf_filtered (_(" (main process)"));
- printf_filtered (_(" at "));
- fputs_filtered (paddress (gdbarch, pc), gdb_stdout);
- sal = find_pc_line (pc, 0);
- if (sal.symtab)
- printf_filtered (_(", file %s"),
- symtab_to_filename_for_display (sal.symtab));
- if (sal.line)
- printf_filtered (_(", line %d"), sal.line);
- if (!sal.symtab && !sal.line)
- {
- struct bound_minimal_symbol msym;
- msym = lookup_minimal_symbol_by_pc (pc);
- if (msym.minsym)
- printf_filtered (", <%s>", MSYMBOL_LINKAGE_NAME (msym.minsym));
- }
- putchar_filtered ('\n');
- }
- if (printed == NULL)
- {
- if (requested > 0)
- printf_filtered (_("No checkpoint number %d.\n"), requested);
- else
- printf_filtered (_("No checkpoints.\n"));
- }
- }
- static int checkpointing_pid = 0;
- int
- linux_fork_checkpointing_p (int pid)
- {
- return (checkpointing_pid == pid);
- }
- static int
- inf_has_multiple_thread_cb (struct thread_info *tp, void *data)
- {
- int *count_p = (int *) data;
- if (current_inferior ()->pid == ptid_get_pid (tp->ptid))
- (*count_p)++;
-
- return *count_p > 1;
- }
- static int
- inf_has_multiple_threads (void)
- {
- int count = 0;
- iterate_over_threads (inf_has_multiple_thread_cb, &count);
- return (count > 1);
- }
- static void
- checkpoint_command (char *args, int from_tty)
- {
- struct objfile *fork_objf;
- struct gdbarch *gdbarch;
- struct target_waitstatus last_target_waitstatus;
- ptid_t last_target_ptid;
- struct value *fork_fn = NULL, *ret;
- struct fork_info *fp;
- pid_t retpid;
- struct cleanup *old_chain;
- if (!target_has_execution)
- error (_("The program is not being run."));
-
- update_thread_list ();
- if (inf_has_multiple_threads ())
- error (_("checkpoint: can't checkpoint multiple threads."));
-
- if (lookup_minimal_symbol ("fork", NULL, NULL).minsym != NULL)
- fork_fn = find_function_in_inferior ("fork", &fork_objf);
- if (!fork_fn)
- if (lookup_minimal_symbol ("_fork", NULL, NULL).minsym != NULL)
- fork_fn = find_function_in_inferior ("fork", &fork_objf);
- if (!fork_fn)
- error (_("checkpoint: can't find fork function in inferior."));
- gdbarch = get_objfile_arch (fork_objf);
- ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-
- old_chain = make_cleanup_restore_integer (&checkpointing_pid);
- checkpointing_pid = ptid_get_pid (inferior_ptid);
- ret = call_function_by_hand (fork_fn, 0, &ret);
- do_cleanups (old_chain);
- if (!ret)
- error (_("checkpoint: call_function_by_hand returned null."));
- retpid = value_as_long (ret);
- get_last_target_status (&last_target_ptid, &last_target_waitstatus);
- fp = find_fork_pid (retpid);
- if (from_tty)
- {
- int parent_pid;
- printf_filtered (_("checkpoint %d: fork returned pid %ld.\n"),
- fp != NULL ? fp->num : -1, (long) retpid);
- if (info_verbose)
- {
- parent_pid = ptid_get_lwp (last_target_ptid);
- if (parent_pid == 0)
- parent_pid = ptid_get_pid (last_target_ptid);
- printf_filtered (_(" gdb says parent = %ld.\n"),
- (long) parent_pid);
- }
- }
- if (!fp)
- error (_("Failed to find new fork"));
- fork_save_infrun_state (fp, 1);
- fp->parent_ptid = last_target_ptid;
- }
- static void
- linux_fork_context (struct fork_info *newfp, int from_tty)
- {
-
- struct fork_info *oldfp;
- gdb_assert (newfp != NULL);
- oldfp = find_fork_ptid (inferior_ptid);
- gdb_assert (oldfp != NULL);
- fork_save_infrun_state (oldfp, 1);
- remove_breakpoints ();
- fork_load_infrun_state (newfp);
- insert_breakpoints ();
- printf_filtered (_("Switching to %s\n"),
- target_pid_to_str (inferior_ptid));
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
- }
- static void
- restart_command (char *args, int from_tty)
- {
- struct fork_info *fp;
- if (!args || !*args)
- error (_("Requires argument (checkpoint id to restart)"));
- if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
- error (_("Not found: checkpoint id %s"), args);
- linux_fork_context (fp, from_tty);
- }
- void
- _initialize_linux_fork (void)
- {
- init_fork_list ();
-
- add_com ("checkpoint", class_obscure, checkpoint_command, _("\
- Fork a duplicate process (experimental)."));
-
- add_com ("restart", class_obscure, restart_command, _("\
- restart <n>: restore program context from a checkpoint.\n\
- Argument 'n' is checkpoint ID, as displayed by 'info checkpoints'."));
-
- add_cmd ("checkpoint", class_obscure, delete_checkpoint_command, _("\
- Delete a checkpoint (experimental)."),
- &deletelist);
-
- add_cmd ("checkpoint", class_obscure, detach_checkpoint_command, _("\
- Detach from a checkpoint (experimental)."),
- &detachlist);
-
- add_info ("checkpoints", info_checkpoints_command,
- _("IDs of currently known checkpoints."));
- }