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