* coffcode.h (coff_write_object_contents): Enclose all occurrences
[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 471 struct symtab_and_line sal;
ac264b3b 472 struct fork_info *fp;
ac264b3b 473 ULONGEST pc;
b8db102d
MS
474 int requested = -1;
475 struct fork_info *printed = NULL;
476
477 if (arg && *arg)
478 requested = (int) parse_and_eval_long (arg);
ac264b3b
MS
479
480 for (fp = fork_list; fp; fp = fp->next)
481 {
b8db102d
MS
482 if (requested > 0 && fp->num != requested)
483 continue;
484
485 printed = fp;
ac264b3b
MS
486 if (ptid_equal (fp->ptid, inferior_ptid))
487 {
488 printf_filtered ("* ");
fb14de7b 489 pc = regcache_read_pc (get_current_regcache ());
ac264b3b
MS
490 }
491 else
492 {
493 printf_filtered (" ");
2277426b 494 pc = regcache_read_pc (fp->savedregs);
ac264b3b
MS
495 }
496 printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
497 if (fp->num == 0)
498 printf_filtered (_(" (main process)"));
499 printf_filtered (_(" at "));
5af949e3 500 fputs_filtered (paddress (gdbarch, pc), gdb_stdout);
ac264b3b
MS
501
502 sal = find_pc_line (pc, 0);
503 if (sal.symtab)
504 {
505 char *tmp = strrchr (sal.symtab->filename, '/');
506
507 if (tmp)
508 printf_filtered (_(", file %s"), tmp + 1);
509 else
510 printf_filtered (_(", file %s"), sal.symtab->filename);
511 }
512 if (sal.line)
513 printf_filtered (_(", line %d"), sal.line);
514 if (!sal.symtab && !sal.line)
515 {
516 struct minimal_symbol *msym;
517
518 msym = lookup_minimal_symbol_by_pc (pc);
519 if (msym)
520 printf_filtered (", <%s>", SYMBOL_LINKAGE_NAME (msym));
521 }
522
523 putchar_filtered ('\n');
524 }
b8db102d
MS
525 if (printed == NULL)
526 {
527 if (requested > 0)
2277426b 528 printf_filtered (_("No checkpoint number %d.\n"), requested);
b8db102d 529 else
2277426b 530 printf_filtered (_("No checkpoints.\n"));
b8db102d 531 }
ac264b3b
MS
532}
533
2277426b
PA
534/* The PID of the process we're checkpointing. */
535static int checkpointing_pid = 0;
ac264b3b 536
2277426b
PA
537int
538linux_fork_checkpointing_p (int pid)
ac264b3b 539{
2277426b 540 return (checkpointing_pid == pid);
ac264b3b
MS
541}
542
543static void
544checkpoint_command (char *args, int from_tty)
545{
3e3b026f
UW
546 struct objfile *fork_objf;
547 struct gdbarch *gdbarch;
ac264b3b
MS
548 struct target_waitstatus last_target_waitstatus;
549 ptid_t last_target_ptid;
550 struct value *fork_fn = NULL, *ret;
551 struct fork_info *fp;
552 pid_t retpid;
553 struct cleanup *old_chain;
74960c60 554
ac264b3b
MS
555 /* Make the inferior fork, record its (and gdb's) state. */
556
557 if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
3e3b026f 558 fork_fn = find_function_in_inferior ("fork", &fork_objf);
ac264b3b
MS
559 if (!fork_fn)
560 if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
3e3b026f 561 fork_fn = find_function_in_inferior ("fork", &fork_objf);
ac264b3b
MS
562 if (!fork_fn)
563 error (_("checkpoint: can't find fork function in inferior."));
564
3e3b026f
UW
565 gdbarch = get_objfile_arch (fork_objf);
566 ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
2277426b
PA
567
568 /* Tell linux-nat.c that we're checkpointing this inferior. */
569 old_chain = make_cleanup_restore_integer (&checkpointing_pid);
570 checkpointing_pid = PIDGET (inferior_ptid);
571
ac264b3b
MS
572 ret = call_function_by_hand (fork_fn, 0, &ret);
573 do_cleanups (old_chain);
574 if (!ret) /* Probably can't happen. */
575 error (_("checkpoint: call_function_by_hand returned null."));
576
577 retpid = value_as_long (ret);
578 get_last_target_status (&last_target_ptid, &last_target_waitstatus);
579 if (from_tty)
580 {
581 int parent_pid;
582
3cb5bea9 583 printf_filtered (_("checkpoint: fork returned pid %ld.\n"),
ac264b3b
MS
584 (long) retpid);
585 if (info_verbose)
586 {
587 parent_pid = ptid_get_lwp (last_target_ptid);
588 if (parent_pid == 0)
589 parent_pid = ptid_get_pid (last_target_ptid);
3cb5bea9 590 printf_filtered (_(" gdb says parent = %ld.\n"),
ac264b3b
MS
591 (long) parent_pid);
592 }
593 }
594
595 fp = find_fork_pid (retpid);
596 if (!fp)
597 error (_("Failed to find new fork"));
598 fork_save_infrun_state (fp, 1);
599}
600
601static void
602linux_fork_context (struct fork_info *newfp, int from_tty)
603{
604 /* Now we attempt to switch processes. */
0d14fc63 605 struct fork_info *oldfp;
ac264b3b 606
0d14fc63 607 gdb_assert (newfp != NULL);
ac264b3b 608
0d14fc63
PA
609 oldfp = find_fork_ptid (inferior_ptid);
610 gdb_assert (oldfp != NULL);
ac264b3b
MS
611
612 fork_save_infrun_state (oldfp, 1);
74960c60 613 remove_breakpoints ();
ac264b3b 614 fork_load_infrun_state (newfp);
74960c60 615 insert_breakpoints ();
ac264b3b 616
3cb5bea9 617 printf_filtered (_("Switching to %s\n"),
ac264b3b
MS
618 target_pid_to_str (inferior_ptid));
619
620 print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
621}
622
2277426b 623/* Switch inferior process (checkpoint) context, by checkpoint id. */
ac264b3b
MS
624static void
625restart_command (char *args, int from_tty)
626{
627 struct fork_info *fp;
628
629 if (!args || !*args)
630 error (_("Requires argument (checkpoint id to restart)"));
631
632 if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
633 error (_("Not found: checkpoint id %s"), args);
634
635 linux_fork_context (fp, from_tty);
636}
637
638void
639_initialize_linux_fork (void)
640{
641 init_fork_list ();
642
ac264b3b
MS
643 /* Checkpoint command: create a fork of the inferior process
644 and set it aside for later debugging. */
645
646 add_com ("checkpoint", class_obscure, checkpoint_command, _("\
647Fork a duplicate process (experimental)."));
648
2277426b
PA
649 /* Restart command: restore the context of a specified checkpoint
650 process. */
ac264b3b
MS
651
652 add_com ("restart", class_obscure, restart_command, _("\
653restart <n>: restore program context from a checkpoint.\n\
654Argument 'n' is checkpoint ID, as displayed by 'info checkpoints'."));
655
b8db102d 656 /* Delete checkpoint command: kill the process and remove it from
3cb5bea9 657 the fork list. */
ac264b3b 658
3cb5bea9 659 add_cmd ("checkpoint", class_obscure, delete_checkpoint_command, _("\
2277426b 660Delete a checkpoint (experimental)."),
b8db102d 661 &deletelist);
ac264b3b 662
3cb5bea9 663 /* Detach checkpoint command: release the process to run independently,
ac264b3b
MS
664 and remove it from the fork list. */
665
3cb5bea9 666 add_cmd ("checkpoint", class_obscure, detach_checkpoint_command, _("\
2277426b 667Detach from a checkpoint (experimental)."),
f73adfeb 668 &detachlist);
ac264b3b 669
3cb5bea9 670 /* Info checkpoints command: list all forks/checkpoints
ac264b3b
MS
671 currently under gdb's control. */
672
3cb5bea9 673 add_info ("checkpoints", info_checkpoints_command,
2277426b 674 _("IDs of currently known checkpoints."));
ac264b3b 675}
This page took 0.362746 seconds and 4 git commands to generate.