* linux-fork.c (linux_fork_detach): New.
[deliverable/binutils-gdb.git] / gdb / linux-fork.c
1 /* GNU/Linux native-dependent code for debugging multiple forks.
2
3 Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
5 This file is part of GDB.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include "defs.h"
21 #include "inferior.h"
22 #include "regcache.h"
23 #include "gdbcmd.h"
24 #include "infcall.h"
25 #include "objfiles.h"
26 #include "gdb_assert.h"
27 #include "gdb_string.h"
28 #include "linux-fork.h"
29 #include "linux-nat.h"
30
31 #include <sys/ptrace.h>
32 #include "gdb_wait.h"
33 #include <sys/param.h>
34 #include "gdb_dirent.h"
35 #include <ctype.h>
36
37 struct fork_info *fork_list;
38 static int highest_fork_num;
39
40 /* Prevent warning from -Wmissing-prototypes. */
41 extern void _initialize_linux_fork (void);
42
43 int detach_fork = 1; /* Default behavior is to detach
44 newly forked processes (legacy). */
45
46 /* Fork list data structure: */
47 struct fork_info
48 {
49 struct fork_info *next;
50 ptid_t ptid;
51 int num; /* Convenient handle (GDB fork id) */
52 struct regcache *savedregs; /* Convenient for info fork, saves
53 having to actually switch contexts. */
54 int clobber_regs; /* True if we should restore saved regs. */
55 ULONGEST pc; /* PC for info fork. */
56 off_t *filepos; /* Set of open file descriptors' offsets. */
57 int maxfd;
58 };
59
60 /* Fork list methods: */
61
62 extern int
63 forks_exist_p (void)
64 {
65 return (fork_list != NULL);
66 }
67
68 /* Add a fork to internal fork list.
69 Called from linux child_follow_fork. */
70
71 extern struct fork_info *
72 add_fork (pid_t pid)
73 {
74 struct fork_info *fp;
75
76 if (fork_list == NULL && pid != PIDGET (inferior_ptid))
77 {
78 /* Special case -- if this is the first fork in the list
79 (the list is hitherto empty), and if this new fork is
80 NOT the current inferior_ptid, then add inferior_ptid
81 first, as a special zeroeth fork id. */
82 highest_fork_num = -1;
83 add_fork (PIDGET (inferior_ptid)); /* safe recursion */
84 }
85
86 fp = XZALLOC (struct fork_info);
87 fp->ptid = ptid_build (pid, pid, 0);
88 fp->num = ++highest_fork_num;
89 fp->next = fork_list;
90 fork_list = fp;
91 return fp;
92 }
93
94 static void
95 free_fork (struct fork_info *fp)
96 {
97 /* Notes on step-resume breakpoints: since this is a concern for
98 threads, let's convince ourselves that it's not a concern for
99 forks. There are two ways for a fork_info to be created. First,
100 by the checkpoint command, in which case we're at a gdb prompt
101 and there can't be any step-resume breakpoint. Second, by a fork
102 in the user program, in which case we *may* have stepped into the
103 fork call, but regardless of whether we follow the parent or the
104 child, we will return to the same place and the step-resume
105 breakpoint, if any, will take care of itself as usual. And
106 unlike threads, we do not save a private copy of the step-resume
107 breakpoint -- so we're OK. */
108
109 if (fp)
110 {
111 if (fp->savedregs)
112 regcache_xfree (fp->savedregs);
113 if (fp->filepos)
114 xfree (fp->filepos);
115 xfree (fp);
116 }
117 }
118
119 static void
120 delete_fork (ptid_t ptid)
121 {
122 struct fork_info *fp, *fpprev;
123
124 fpprev = NULL;
125
126 for (fp = fork_list; fp; fpprev = fp, fp = fp->next)
127 if (ptid_equal (fp->ptid, ptid))
128 break;
129
130 if (!fp)
131 return;
132
133 if (fpprev)
134 fpprev->next = fp->next;
135 else
136 fork_list = fp->next;
137
138 free_fork (fp);
139
140 /* Special case: if there is now only one process in the list,
141 and if it is (hopefully!) the current inferior_ptid, then
142 remove it, leaving the list empty -- we're now down to the
143 default case of debugging a single process. */
144 if (fork_list != NULL && fork_list->next == NULL &&
145 ptid_equal (fork_list->ptid, inferior_ptid))
146 {
147 /* Last fork -- delete from list and handle as solo process
148 (should be a safe recursion). */
149 delete_fork (inferior_ptid);
150 }
151 }
152
153 /* Find a fork_info by matching PTID. */
154 static struct fork_info *
155 find_fork_ptid (ptid_t ptid)
156 {
157 struct fork_info *fp;
158
159 for (fp = fork_list; fp; fp = fp->next)
160 if (ptid_equal (fp->ptid, ptid))
161 return fp;
162
163 return NULL;
164 }
165
166 /* Find a fork_info by matching ID. */
167 static struct fork_info *
168 find_fork_id (int num)
169 {
170 struct fork_info *fp;
171
172 for (fp = fork_list; fp; fp = fp->next)
173 if (fp->num == num)
174 return fp;
175
176 return NULL;
177 }
178
179 /* Find a fork_info by matching pid. */
180 extern struct fork_info *
181 find_fork_pid (pid_t pid)
182 {
183 struct fork_info *fp;
184
185 for (fp = fork_list; fp; fp = fp->next)
186 if (pid == ptid_get_pid (fp->ptid))
187 return fp;
188
189 return NULL;
190 }
191
192 static ptid_t
193 fork_id_to_ptid (int num)
194 {
195 struct fork_info *fork = find_fork_id (num);
196 if (fork)
197 return fork->ptid;
198 else
199 return pid_to_ptid (-1);
200 }
201
202 static void
203 init_fork_list (void)
204 {
205 struct fork_info *fp, *fpnext;
206
207 if (!fork_list)
208 return;
209
210 for (fp = fork_list; fp; fp = fpnext)
211 {
212 fpnext = fp->next;
213 delete_inferior (ptid_get_pid (fp->ptid));
214 free_fork (fp);
215 }
216
217 fork_list = NULL;
218 }
219
220 /* Fork list <-> gdb interface. */
221
222 /* Utility function for fork_load/fork_save.
223 Calls lseek in the (current) inferior process. */
224
225 static off_t
226 call_lseek (int fd, off_t offset, int whence)
227 {
228 char exp[80];
229
230 snprintf (&exp[0], sizeof (exp), "lseek (%d, %ld, %d)",
231 fd, (long) offset, whence);
232 return (off_t) parse_and_eval_long (&exp[0]);
233 }
234
235 /* Load infrun state for the fork PTID. */
236
237 static void
238 fork_load_infrun_state (struct fork_info *fp)
239 {
240 extern void nullify_last_target_wait_ptid ();
241 int i;
242
243 inferior_ptid = fp->ptid;
244
245 linux_nat_switch_fork (inferior_ptid);
246
247 if (fp->savedregs && fp->clobber_regs)
248 regcache_cpy (get_current_regcache (), fp->savedregs);
249
250 registers_changed ();
251 reinit_frame_cache ();
252
253 stop_pc = read_pc ();
254 nullify_last_target_wait_ptid ();
255
256 /* Now restore the file positions of open file descriptors. */
257 if (fp->filepos)
258 {
259 for (i = 0; i <= fp->maxfd; i++)
260 if (fp->filepos[i] != (off_t) -1)
261 call_lseek (i, fp->filepos[i], SEEK_SET);
262 /* NOTE: I can get away with using SEEK_SET and SEEK_CUR because
263 this is native-only. If it ever has to be cross, we'll have
264 to rethink this. */
265 }
266 }
267
268 /* Save infrun state for the fork PTID.
269 Exported for use by linux child_follow_fork. */
270
271 extern void
272 fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
273 {
274 char path[MAXPATHLEN];
275 struct dirent *de;
276 DIR *d;
277
278 if (fp->savedregs)
279 regcache_xfree (fp->savedregs);
280
281 fp->savedregs = regcache_dup (get_current_regcache ());
282 fp->clobber_regs = clobber_regs;
283 fp->pc = read_pc ();
284
285 if (clobber_regs)
286 {
287 /* Now save the 'state' (file position) of all open file descriptors.
288 Unfortunately fork does not take care of that for us... */
289 snprintf (path, MAXPATHLEN, "/proc/%ld/fd", (long) PIDGET (fp->ptid));
290 if ((d = opendir (path)) != NULL)
291 {
292 long tmp;
293
294 fp->maxfd = 0;
295 while ((de = readdir (d)) != NULL)
296 {
297 /* Count open file descriptors (actually find highest
298 numbered). */
299 tmp = strtol (&de->d_name[0], NULL, 10);
300 if (fp->maxfd < tmp)
301 fp->maxfd = tmp;
302 }
303 /* Allocate array of file positions. */
304 fp->filepos = xrealloc (fp->filepos,
305 (fp->maxfd + 1) * sizeof (*fp->filepos));
306
307 /* Initialize to -1 (invalid). */
308 for (tmp = 0; tmp <= fp->maxfd; tmp++)
309 fp->filepos[tmp] = -1;
310
311 /* Now find actual file positions. */
312 rewinddir (d);
313 while ((de = readdir (d)) != NULL)
314 if (isdigit (de->d_name[0]))
315 {
316 tmp = strtol (&de->d_name[0], NULL, 10);
317 fp->filepos[tmp] = call_lseek (tmp, 0, SEEK_CUR);
318 }
319 closedir (d);
320 }
321 }
322 }
323
324 /* Kill 'em all, let God sort 'em out... */
325
326 extern void
327 linux_fork_killall (void)
328 {
329 /* Walk list and kill every pid. No need to treat the
330 current inferior_ptid as special (we do not return a
331 status for it) -- however any process may be a child
332 or a parent, so may get a SIGCHLD from a previously
333 killed child. Wait them all out. */
334 struct fork_info *fp;
335 pid_t pid, ret;
336 int status;
337
338 for (fp = fork_list; fp; fp = fp->next)
339 {
340 pid = PIDGET (fp->ptid);
341 do {
342 /* Use SIGKILL instead of PTRACE_KILL because the former works even
343 if the thread is running, while the later doesn't. */
344 kill (pid, SIGKILL);
345 ret = waitpid (pid, &status, 0);
346 /* We might get a SIGCHLD instead of an exit status. This is
347 aggravated by the first kill above - a child has just
348 died. MVS comment cut-and-pasted from linux-nat. */
349 } while (ret == pid && WIFSTOPPED (status));
350 }
351 init_fork_list (); /* Clear list, prepare to start fresh. */
352 }
353
354 /* The current inferior_ptid has exited, but there are other viable
355 forks to debug. Delete the exiting one and context-switch to the
356 first available. */
357
358 extern void
359 linux_fork_mourn_inferior (void)
360 {
361 /* Wait just one more time to collect the inferior's exit status.
362 Do not check whether this succeeds though, since we may be
363 dealing with a process that we attached to. Such a process will
364 only report its exit status to its original parent. */
365 int status;
366
367 waitpid (ptid_get_pid (inferior_ptid), &status, 0);
368
369 /* OK, presumably inferior_ptid is the one who has exited.
370 We need to delete that one from the fork_list, and switch
371 to the next available fork. */
372 delete_fork (inferior_ptid);
373 /* Delete process from GDB's inferior list. */
374 delete_inferior (ptid_get_pid (inferior_ptid));
375
376 /* There should still be a fork - if there's only one left,
377 delete_fork won't remove it, because we haven't updated
378 inferior_ptid yet. */
379 gdb_assert (fork_list);
380
381 fork_load_infrun_state (fork_list);
382 printf_filtered (_("[Switching to %s]\n"),
383 target_pid_to_str (inferior_ptid));
384
385 /* If there's only one fork, switch back to non-fork mode. */
386 if (fork_list->next == NULL)
387 delete_fork (inferior_ptid);
388 }
389
390 /* The current inferior_ptid is being detached, but there are other
391 viable forks to debug. Detach and delete it and context-switch to
392 the first available. */
393
394 extern void
395 linux_fork_detach (char *args, int from_tty)
396 {
397 /* OK, inferior_ptid is the one we are detaching from. We need to
398 delete it from the fork_list, and switch to the next available
399 fork. */
400
401 if (ptrace (PTRACE_DETACH, PIDGET (inferior_ptid), 0, 0))
402 error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
403
404 delete_fork (inferior_ptid);
405 /* Delete process from GDB's inferior list. */
406 delete_inferior (ptid_get_pid (inferior_ptid));
407
408 /* There should still be a fork - if there's only one left,
409 delete_fork won't remove it, because we haven't updated
410 inferior_ptid yet. */
411 gdb_assert (fork_list);
412
413 fork_load_infrun_state (fork_list);
414
415 if (from_tty)
416 printf_filtered (_("[Switching to %s]\n"),
417 target_pid_to_str (inferior_ptid));
418
419 /* If there's only one fork, switch back to non-fork mode. */
420 if (fork_list->next == NULL)
421 delete_fork (inferior_ptid);
422 }
423
424 /* Fork list <-> user interface. */
425
426 static void
427 delete_fork_command (char *args, int from_tty)
428 {
429 ptid_t ptid;
430
431 if (!args || !*args)
432 error (_("Requires argument (fork/checkpoint id to delete)"));
433
434 ptid = fork_id_to_ptid (parse_and_eval_long (args));
435 if (ptid_equal (ptid, minus_one_ptid))
436 error (_("No such fork/checkpoint id, %s"), args);
437
438 if (ptid_equal (ptid, inferior_ptid))
439 error (_("Please switch to another fork/checkpoint before deleting the current one"));
440
441 if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
442 error (_("Unable to kill pid %s"), target_tid_to_str (ptid));
443
444 if (from_tty)
445 printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
446
447 delete_fork (ptid);
448 /* Delete process from GDB's inferior list. */
449 delete_inferior (ptid_get_pid (ptid));
450 }
451
452 static void
453 detach_fork_command (char *args, int from_tty)
454 {
455 ptid_t ptid;
456
457 if (!args || !*args)
458 error (_("Requires argument (fork id to detach)"));
459
460 ptid = fork_id_to_ptid (parse_and_eval_long (args));
461 if (ptid_equal (ptid, minus_one_ptid))
462 error (_("No such fork id, %s"), args);
463
464 if (ptid_equal (ptid, inferior_ptid))
465 error (_("Please switch to another fork before detaching the current one"));
466
467 if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
468 error (_("Unable to detach %s"), target_pid_to_str (ptid));
469
470 if (from_tty)
471 printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
472
473 delete_fork (ptid);
474 /* Delete process from GDB's process table. */
475 detach_inferior (ptid_get_pid (ptid));
476 }
477
478 /* Print information about currently known forks. */
479
480 static void
481 info_forks_command (char *arg, int from_tty)
482 {
483 struct frame_info *cur_frame;
484 struct symtab_and_line sal;
485 struct symtab *cur_symtab;
486 struct fork_info *fp;
487 int cur_line;
488 ULONGEST pc;
489 int requested = -1;
490 struct fork_info *printed = NULL;
491
492 if (arg && *arg)
493 requested = (int) parse_and_eval_long (arg);
494
495 for (fp = fork_list; fp; fp = fp->next)
496 {
497 if (requested > 0 && fp->num != requested)
498 continue;
499
500 printed = fp;
501 if (ptid_equal (fp->ptid, inferior_ptid))
502 {
503 printf_filtered ("* ");
504 pc = read_pc ();
505 }
506 else
507 {
508 printf_filtered (" ");
509 pc = fp->pc;
510 }
511 printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
512 if (fp->num == 0)
513 printf_filtered (_(" (main process)"));
514 printf_filtered (_(" at "));
515 fputs_filtered (paddress (pc), gdb_stdout);
516
517 sal = find_pc_line (pc, 0);
518 if (sal.symtab)
519 {
520 char *tmp = strrchr (sal.symtab->filename, '/');
521
522 if (tmp)
523 printf_filtered (_(", file %s"), tmp + 1);
524 else
525 printf_filtered (_(", file %s"), sal.symtab->filename);
526 }
527 if (sal.line)
528 printf_filtered (_(", line %d"), sal.line);
529 if (!sal.symtab && !sal.line)
530 {
531 struct minimal_symbol *msym;
532
533 msym = lookup_minimal_symbol_by_pc (pc);
534 if (msym)
535 printf_filtered (", <%s>", SYMBOL_LINKAGE_NAME (msym));
536 }
537
538 putchar_filtered ('\n');
539 }
540 if (printed == NULL)
541 {
542 if (requested > 0)
543 printf_filtered (_("No fork number %d.\n"), requested);
544 else
545 printf_filtered (_("No forks.\n"));
546 }
547 }
548
549 /* Save/restore mode variable 'detach_fork':
550 We need to temporarily take over this mode variable, while
551 preserving the user-specified state, and make sure that it
552 gets restored in case of error.
553
554 The int pointer that we use comes from the caller, so we can
555 be called more than once (even though currently we don't need to). */
556
557 static void
558 restore_detach_fork (void *arg)
559 {
560 detach_fork = *(int *) arg;
561 }
562
563 static struct cleanup *
564 save_detach_fork (int *saved_val)
565 {
566 *saved_val = detach_fork;
567 return make_cleanup (restore_detach_fork, (void *) saved_val);
568 }
569
570 static void
571 checkpoint_command (char *args, int from_tty)
572 {
573 struct objfile *fork_objf;
574 struct gdbarch *gdbarch;
575 struct target_waitstatus last_target_waitstatus;
576 ptid_t last_target_ptid;
577 struct value *fork_fn = NULL, *ret;
578 struct fork_info *fp;
579 pid_t retpid;
580 struct cleanup *old_chain;
581 long i;
582 /* Make this temp var static, 'cause it's used in the error context. */
583 static int temp_detach_fork;
584
585 /* Remove breakpoints, so that they are not inserted
586 in the forked process. */
587 remove_breakpoints ();
588
589 /* Make the inferior fork, record its (and gdb's) state. */
590
591 if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
592 fork_fn = find_function_in_inferior ("fork", &fork_objf);
593 if (!fork_fn)
594 if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
595 fork_fn = find_function_in_inferior ("fork", &fork_objf);
596 if (!fork_fn)
597 error (_("checkpoint: can't find fork function in inferior."));
598
599 gdbarch = get_objfile_arch (fork_objf);
600 ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
601 old_chain = save_detach_fork (&temp_detach_fork);
602 detach_fork = 0;
603 ret = call_function_by_hand (fork_fn, 0, &ret);
604 do_cleanups (old_chain);
605 if (!ret) /* Probably can't happen. */
606 error (_("checkpoint: call_function_by_hand returned null."));
607
608 retpid = value_as_long (ret);
609 get_last_target_status (&last_target_ptid, &last_target_waitstatus);
610 if (from_tty)
611 {
612 int parent_pid;
613
614 printf_filtered (_("checkpoint: fork returned pid %ld.\n"),
615 (long) retpid);
616 if (info_verbose)
617 {
618 parent_pid = ptid_get_lwp (last_target_ptid);
619 if (parent_pid == 0)
620 parent_pid = ptid_get_pid (last_target_ptid);
621 printf_filtered (_(" gdb says parent = %ld.\n"),
622 (long) parent_pid);
623 }
624 }
625
626 fp = find_fork_pid (retpid);
627 if (!fp)
628 error (_("Failed to find new fork"));
629 fork_save_infrun_state (fp, 1);
630 insert_breakpoints ();
631 }
632
633 static void
634 linux_fork_context (struct fork_info *newfp, int from_tty)
635 {
636 /* Now we attempt to switch processes. */
637 struct fork_info *oldfp;
638 ptid_t ptid;
639 int id, i;
640
641 gdb_assert (newfp != NULL);
642
643 oldfp = find_fork_ptid (inferior_ptid);
644 gdb_assert (oldfp != NULL);
645
646 fork_save_infrun_state (oldfp, 1);
647 remove_breakpoints ();
648 fork_load_infrun_state (newfp);
649 insert_breakpoints ();
650
651 printf_filtered (_("Switching to %s\n"),
652 target_pid_to_str (inferior_ptid));
653
654 print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
655 }
656
657 /* Switch inferior process (fork) context, by process id. */
658 static void
659 process_command (char *args, int from_tty)
660 {
661 struct fork_info *fp;
662
663 if (!args || !*args)
664 error (_("Requires argument (process id to switch to)"));
665
666 if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
667 error (_("Not found: process id %s"), args);
668
669 linux_fork_context (fp, from_tty);
670 }
671
672 /* Switch inferior process (fork) context, by fork id. */
673 static void
674 fork_command (char *args, int from_tty)
675 {
676 struct fork_info *fp;
677
678 if (!args || !*args)
679 error (_("Requires argument (fork id to switch to)"));
680
681 if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
682 error (_("Not found: fork id %s"), args);
683
684 linux_fork_context (fp, from_tty);
685 }
686
687 /* Switch inferior process (fork) context, by checkpoint id. */
688 static void
689 restart_command (char *args, int from_tty)
690 {
691 struct fork_info *fp;
692
693 if (!args || !*args)
694 error (_("Requires argument (checkpoint id to restart)"));
695
696 if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
697 error (_("Not found: checkpoint id %s"), args);
698
699 linux_fork_context (fp, from_tty);
700 }
701
702 void
703 _initialize_linux_fork (void)
704 {
705 init_fork_list ();
706
707 /* Set/show detach-on-fork: user-settable mode. */
708
709 add_setshow_boolean_cmd ("detach-on-fork", class_obscure, &detach_fork, _("\
710 Set whether gdb will detach the child of a fork."), _("\
711 Show whether gdb will detach the child of a fork."), _("\
712 Tells gdb whether to detach the child of a fork."),
713 NULL, NULL, &setlist, &showlist);
714
715 /* Set/show restart-auto-finish: user-settable count. Causes the
716 first "restart" of a fork to do some number of "finish" commands
717 before returning to user.
718
719 Useful because otherwise the virgin fork process will be stopped
720 somewhere in the un-interesting fork system call. */
721
722 /* Checkpoint command: create a fork of the inferior process
723 and set it aside for later debugging. */
724
725 add_com ("checkpoint", class_obscure, checkpoint_command, _("\
726 Fork a duplicate process (experimental)."));
727
728 /* Restart command: restore the context of a specified fork
729 process. May be used for "program forks" as well as for
730 "debugger forks" (checkpoints). */
731
732 add_com ("restart", class_obscure, restart_command, _("\
733 restart <n>: restore program context from a checkpoint.\n\
734 Argument 'n' is checkpoint ID, as displayed by 'info checkpoints'."));
735
736 /* Delete checkpoint command: kill the process and remove it from
737 fork list. */
738
739 add_cmd ("checkpoint", class_obscure, delete_fork_command, _("\
740 Delete a fork/checkpoint (experimental)."),
741 &deletelist);
742
743 /* Detach checkpoint command: release the process to run independently,
744 and remove it from the fork list. */
745
746 add_cmd ("checkpoint", class_obscure, detach_fork_command, _("\
747 Detach from a fork/checkpoint (experimental)."),
748 &detachlist);
749
750 /* Info checkpoints command: list all forks/checkpoints
751 currently under gdb's control. */
752
753 add_info ("checkpoints", info_forks_command,
754 _("IDs of currently known forks/checkpoints."));
755
756 /* Command aliases (let "fork" and "checkpoint" be used
757 interchangeably). */
758
759 add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
760 add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
761 add_info_alias ("forks", "checkpoints", 0);
762
763 /* "fork <n>" (by analogy to "thread <n>"). */
764 add_com ("fork", class_obscure, fork_command, _("\
765 fork <n>: Switch between forked processes.\n\
766 Argument 'n' is fork ID, as displayed by 'info forks'."));
767
768 /* "process <proc id>" as opposed to "fork <fork id>". */
769 add_com ("process", class_obscure, process_command, _("\
770 process <pid>: Switch between forked processes.\n\
771 Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
772 }
This page took 0.060208 seconds and 5 git commands to generate.