Re: [RFC] add 'save-breakpoints' command

Klee Dienes
2001-12-11T08:28:00Z

> There are several mentions of future breaks; you should probably
> clean those up until you're ready to submit that feature.

Rather than try to remove the future-break portions of the
'save-breakpoints' command, and have to re-add them later, it probably
makes more sense to just submit both 'save-breakpoints' and
'future-break' as a combined patch.

In addition to the 'save-breakpoints' support already submitted, this
patch adds a new command 'future-break', that works like break except
that when it encounters an error, instead of returning to top-level,
it saves the expression away and re-tries it every time new symbols or
shared libraries are loaded into the program. It also adds a '-f'
option to the -break-insert MI command, with similar behavior.

2001-12-04 Klee Dienes <kdienes@apple.com>
Ira L. Ruben <ira@apple.com>
James Ingham <jingham@apple.com>

* ui-out.c, ui-out.h: Add ui_out_delete.

* utils.c, defs.h: Add make_cleanup_ui_out_delete.

* mi/mi-cmd-break.c, gdb.h (mi_cmd_break_insert): Add support for
'-f' (future) flag to '-break-insert'.

* doc/gdb.textinfo: Add documentation for 'save-breakpoints' and
'future-break'.

* tracepoint.c (tracepoint_save_command): Fix fopen error reporting to
show errno information just like save-breakpoints command.

* main.c (captured_main): Initialize $input_radix and $output_radix.
These are referenced by a save-breakpoints file to preserve radix
across the breakpoint restoration.

* valprint.c (set_input_radix_1): Set $input_radix.
(set_output_radix_1): Set $output_radix.

* breakpoint.h (struct breakpoint): Add 'original_flags' field to
store original breakpoint flags.

* breakpoint.c:
Add BP_FUTUREFLAG as an argument to break_command_1 to specify a
future-breakpoint.
(write_one_breakpoint): new routine for save_breakpoints_command.
(save_breakpoints_command): Add save_breakpoints_command.
(future_break_command): Add future_break_command..
(_initialize_breakpoint): Define save-breakpoints and its aliases.
Define future-break and its aliases. Add 'break <x> if <expr>'
syntax to the documentation for 'break'.
(gdb_breakpoint): Pass futureflag to do_captured_breakpoint.
(create_breakpoints): Pass 'origflags' field to store for use by
save-breakpoints_command.
(break_command_1, do_captured_breakpoint): Consolidate shared code
from break_command_1 into do_captured_breakpoint. break_command_1
is now a wrapper around do_captured_breakpoint that also processes
future-breakpoints. One side-effect of this is that
mi_cmd_break_insert will now process 'break <x> if <y>" and "break
<x> thread <n>" semantics, overriding any other values passed as
part of the command. It also requires that break commands with
both a thread and a condition take the form 'break <x> thread <n>
if <y>' --- but given the free-form nature of 'if'-expressions,
this seems like a reasonable restriction.
(parse_breakpoint_sals): Handle case where address
is NULL and default_breakpoint_valid to set addr_string to "*pc"
(sal.pc) to avoid warning from breakpoint_re_set_one().
(print_one_breakpoint): Print the address string of
an unset future breakpoint. This was coming up empty before the
change.
(captured_parse_breakpoint_sals): new function, so errors can
be caught in gdb_breakpoint so I can implement future
break in the mi.
(insert_breakpoints): Contain messages for enabling/disabling
breakpoints to one line.
(disable_breakpoints_in_shlibs): Ditto.
breakpoints to one line.
(re_enable_breakpoints_in_shlibs): Ditto.
(breakpoint_re_set, breakpoint_re_set_all, breakpoint_update):
Instead of re-parsing all deferred breakpoints every time
breakpoint_re_set is called, increment a generation number.
When breakpoints need to be up-to-date, call breakpoint_update.
This prevents unnecessary re-parsing of breakpoint information
(and massive future-break spam) when multiple shared libraries
are loaded at the same time.

Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.57
diff -u -r1.57 breakpoint.c
--- breakpoint.c 2001/11/11 16:39:59 1.57
+++ breakpoint.c 2001/12/11 08:17:39
@@ -42,6 +42,10 @@
#include "linespec.h"
#include "completer.h"
#include "gdb.h"
+#include "top.h"
+#include "cli-out.h"
+#include <time.h>
+#include <locale.h>
#ifdef UI_OUT
#include "ui-out.h"
#endif
@@ -720,6 +724,7 @@
static char message1[] = "Error inserting catchpoint %d:\n";
static char message[sizeof (message1) + 30];

+ breakpoint_update ();

ALL_BREAKPOINTS_SAFE (b, temp)
{
@@ -780,17 +785,16 @@
if (!disabled_breaks)
{
target_terminal_ours_for_output ();
- warning ("Cannot insert breakpoint %d:", b->number);
- warning ("Temporarily disabling shared library breakpoints:");
+ printf_filtered ("Temporarily disabling shared library breakpoints:");
}
disabled_breaks = 1;
- warning ("breakpoint #%d ", b->number);
+ printf_filtered (" %d ", b->number);
}
else
#endif
{
target_terminal_ours_for_output ();
- warning ("Cannot insert breakpoint %d:", b->number);
+ warning ("Cannot insert breakpoint %d; disabling it.", b->number);
#ifdef ONE_PROCESS_WRITETEXT
warning ("The same program may be running in another process.");
#endif
@@ -1014,6 +1018,8 @@
return_val = val; /* remember failure */
}
}
+ if (disabled_breaks)
+ printf_filtered ("\n");

return return_val;
}
@@ -2285,6 +2291,7 @@

#define BP_TEMPFLAG 1
#define BP_HARDWAREFLAG 2
+#define BP_FUTUREFLAG 4

/* Check watchpoint condition. */

@@ -3363,11 +3370,16 @@
ui_out_text (uiout, ":");
ui_out_field_int (uiout, "line", b->line_number);
}
- else
+ else if (b->address != 0)
{
- print_address_symbolic (b->address, stb->stream, demangle, "");
- ui_out_field_stream (uiout, "at", stb);
- }
+ print_address_symbolic (b->address, stb->stream, demangle, "");
+ ui_out_field_stream (uiout, "at", stb);
+ }
+ else
+ {
+ /* This prints the user's string from "future break" */
+ ui_out_field_string (uiout, "future-address", b->addr_string);
+ }
#else
if (addressprint)
{
@@ -4114,14 +4126,16 @@
if (!disabled_shlib_breaks)
{
target_terminal_ours_for_output ();
- warning ("Temporarily disabling shared library breakpoints:");
+ printf_filtered ("Temporarily disabling shared library breakpoints:");
}
disabled_shlib_breaks = 1;
- warning ("breakpoint #%d ", b->number);
+ printf_filtered (" %d ", b->number);
}
}
-#endif
}
+#endif
+ if (!silent && disabled_shlib_breaks)
+ printf_filtered ("\n");
}

/* Try to reenable any breakpoints in shared libraries. */
@@ -4129,18 +4143,35 @@
re_enable_breakpoints_in_shlibs (void)
{
struct breakpoint *b;
+ int enabled_shlib_breaks = 0;
+ int silent = 0;

ALL_BREAKPOINTS (b)
if (b->enable_state == bp_shlib_disabled)
- {
- char buf[1];
+ {
+ char buf[1];

- /* Do not reenable the breakpoint if the shared library
- is still not mapped in. */
- if (target_read_memory (b->address, buf, 1) == 0)
- b->enable_state = bp_enabled;
- }
-}
+ /* Do not reenable the breakpoint if the shared library
+ is still not mapped in. */
+ if (target_read_memory (b->address, buf, 1) == 0)
+ {
+ b->enable_state = bp_enabled;
+ if (!silent)
+ {
+ if (!enabled_shlib_breaks)
+ {
+ target_terminal_ours_for_output ();
+ printf_filtered ("Re-enabling shared library breakpoints:");
+ }
+ enabled_shlib_breaks = 1;
+ printf_filtered (" %d", b->number);
+ }
+ }
+ }
+
+ if (!silent && enabled_shlib_breaks)
+ printf_filtered ("\n");
+}

#endif

@@ -4612,7 +4643,8 @@
create_breakpoints (struct symtabs_and_lines sals, char **addr_string,
struct expression **cond, char **cond_string,
enum bptype type, enum bpdisp disposition,
- int thread, int ignore_count, int from_tty)
+ int origflags, int thread, int ignore_count,
+ int from_tty)
{
if (type == bp_hardware_breakpoint)
{
@@ -4638,6 +4670,7 @@
describe_other_breakpoints (sal.pc, sal.section);

b = set_raw_breakpoint (sal, type);
+ b->original_flags = origflags;
set_breakpoint_count (breakpoint_count + 1);
b->number = breakpoint_count;
b->cond = cond[i];
@@ -4672,6 +4705,7 @@
if (default_breakpoint_valid)
{
struct symtab_and_line sal;
+ char *s;
INIT_SAL (&sal); /* initialize to zeroes */
sals->sals = (struct symtab_and_line *)
xmalloc (sizeof (struct symtab_and_line));
@@ -4681,6 +4715,11 @@
sal.section = find_pc_overlay (sal.pc);
sals->sals[0] = sal;
sals->nelts = 1;
+ /* Supply a "*ADDR" for default case (ADDR is pc value). */
+ s = paddr_u (sal.pc);
+ *addr_string = (char **) xmalloc (sizeof (char **));
+ **addr_string = (char *) xmalloc (strlen (s) + 2);
+ sprintf (**addr_string, "*%s", s);
}
else
error ("No default breakpoint address now.");
@@ -4751,140 +4790,6 @@
}
}

-/* Set a breakpoint according to ARG (function, linenum or *address)
- flag: first bit : 0 non-temporary, 1 temporary.
- second bit : 0 normal breakpoint, 1 hardware breakpoint. */
-
-static void
-break_command_1 (char *arg, int flag, int from_tty)
-{
- int tempflag, hardwareflag;
- struct symtabs_and_lines sals;
- register struct expression **cond = 0;
- /* Pointers in arg to the start, and one past the end, of the
- condition. */
- char **cond_string = (char **) NULL;
- char *addr_start = arg;
- char **addr_string;
- struct cleanup *old_chain;
- struct cleanup *breakpoint_chain = NULL;
- int i;
- int thread = -1;
- int ignore_count = 0;
-
- hardwareflag = flag & BP_HARDWAREFLAG;
- tempflag = flag & BP_TEMPFLAG;
-
- sals.sals = NULL;
- sals.nelts = 0;
- addr_string = NULL;
- parse_breakpoint_sals (&arg, &sals, &addr_string);
-
- if (!sals.nelts)
- return;
-
- /* Create a chain of things that always need to be cleaned up. */
- old_chain = make_cleanup (null_cleanup, 0);
-
- /* Make sure that all storage allocated to SALS gets freed. */
- make_cleanup (xfree, sals.sals);
-
- /* Cleanup the addr_string array but not its contents. */
- make_cleanup (xfree, addr_string);
-
- /* Allocate space for all the cond expressions. */
- cond = xcalloc (sals.nelts, sizeof (struct expression *));
- make_cleanup (xfree, cond);
-
- /* Allocate space for all the cond strings. */
- cond_string = xcalloc (sals.nelts, sizeof (char **));
- make_cleanup (xfree, cond_string);
-
- /* ----------------------------- SNIP -----------------------------
- Anything added to the cleanup chain beyond this point is assumed
- to be part of a breakpoint. If the breakpoint create succeeds
- then the memory is not reclaimed. */
- breakpoint_chain = make_cleanup (null_cleanup, 0);
-
- /* Mark the contents of the addr_string for cleanup. These go on
- the breakpoint_chain and only occure if the breakpoint create
- fails. */
- for (i = 0; i < sals.nelts; i++)
- {
- if (addr_string[i] != NULL)
- make_cleanup (xfree, addr_string[i]);
- }
-
- /* Resolve all line numbers to PC's and verify that the addresses
- are ok for the target. */
- breakpoint_sals_to_pc (&sals, addr_start);
-
- /* Verify that condition can be parsed, before setting any
- breakpoints. Allocate a separate condition expression for each
- breakpoint. */
- thread = -1; /* No specific thread yet */
- for (i = 0; i < sals.nelts; i++)
- {
- char *tok = arg;
- while (tok && *tok)
- {
- char *end_tok;
- int toklen;
- char *cond_start = NULL;
- char *cond_end = NULL;
- while (*tok == ' ' || *tok == '\t')
- tok++;
-
- end_tok = tok;
-
- while (*end_tok != ' ' && *end_tok != '\t' && *end_tok != '\000')
- end_tok++;
-
- toklen = end_tok - tok;
-
- if (toklen >= 1 && strncmp (tok, "if", toklen) == 0)
- {
- tok = cond_start = end_tok + 1;
- cond[i] = parse_exp_1 (&tok, block_for_pc (sals.sals[i].pc), 0);
- make_cleanup (xfree, cond[i]);
- cond_end = tok;
- cond_string[i] = savestring (cond_start, cond_end - cond_start);
- make_cleanup (xfree, cond_string[i]);
- }
- else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
- {
- char *tmptok;
-
- tok = end_tok + 1;
- tmptok = tok;
- thread = strtol (tok, &tok, 0);
- if (tok == tmptok)
- error ("Junk after thread keyword.");
- if (!valid_thread_id (thread))
- error ("Unknown thread %d\n", thread);
- }
- else
- error ("Junk at end of arguments.");
- }
- }
-
- create_breakpoints (sals, addr_string, cond, cond_string,
- hardwareflag ? bp_hardware_breakpoint : bp_breakpoint,
- tempflag ? disp_del : disp_donttouch,
- thread, ignore_count, from_tty);
-
- if (sals.nelts > 1)
- {
- warning ("Multiple breakpoints were set.");
- warning ("Use the \"delete\" command to delete unwanted breakpoints.");
- }
- /* That's it. Discard the cleanups for data inserted into the
- breakpoint. */
- discard_cleanups (breakpoint_chain);
- /* But cleanup everything else. */
- do_cleanups (old_chain);
-}
-
/* Set a breakpoint of TYPE/DISPOSITION according to ARG (function,
linenum or *address) with COND and IGNORE_COUNT. */

@@ -4894,10 +4799,29 @@
char *condition;
int hardwareflag;
int tempflag;
+ int futureflag;
int thread;
int ignore_count;
+ int from_tty;
};

+struct captured_parse_breakpoint_sals_args
+ {
+ char **address;
+ struct symtabs_and_lines *sals;
+ char ***addr_string;
+ };
+
+static int
+captured_parse_breakpoint_sals (void *data)
+{
+ struct captured_parse_breakpoint_sals_args *args
+ = (struct captured_parse_breakpoint_sals_args *) data;
+
+ parse_breakpoint_sals (args->address, args->sals, args->addr_string);
+ return (int) GDB_RC_OK;
+}
+
static int
do_captured_breakpoint (void *data)
{
@@ -4909,8 +4833,14 @@
int i;
char **addr_string;
char **cond_string;
-
char *address_end;
+ int orig_flags;
+ int rc = GDB_RC_OK;
+
+ orig_flags =
+ (args->hardwareflag ? BP_HARDWAREFLAG : 0)
+ | (args->tempflag ? BP_TEMPFLAG : 0)
+ | (args->futureflag ? BP_FUTUREFLAG : 0);

/* Parse the source and lines spec. Delay check that the expression
didn't contain trailing garbage until after cleanups are in
@@ -4919,9 +4849,55 @@
sals.nelts = 0;
address_end = args->address;
addr_string = NULL;
- parse_breakpoint_sals (&address_end, &sals, &addr_string);

- if (!sals.nelts)
+ if (args->futureflag)
+ {
+ /* This is gross, but we need to catch errors here, since
+ that is how we are going to find out that this function
+ could not be found... But we don't want to change the
+ error behavior in the case where we are directly trying
+ to set a breakpoint. If only gdb had a reasonable error
+ handling scheme, rather than this stupid longjmp foolishness!
+ */
+ struct captured_parse_breakpoint_sals_args parse_args;
+ parse_args.address = &address_end;
+ parse_args.sals = &sals;
+ parse_args.addr_string = &addr_string;
+
+ rc = catch_errors (captured_parse_breakpoint_sals, &parse_args,
+ NULL, RETURN_MASK_ALL);
+ }
+ else
+ {
+ parse_breakpoint_sals (&address_end, &sals, &addr_string);
+ }
+
+ if (args->futureflag && rc != GDB_RC_OK)
+ {
+ if (args->futureflag)
+ {
+ struct symtab_and_line sal = {0, 0};
+ struct breakpoint *b = set_raw_breakpoint (sal, bp_breakpoint);
+
+ b->number = ++breakpoint_count;
+ b->addr_string = savestring (args->address, strlen (args->address));
+ b->enable_state = bp_shlib_disabled;
+ b->inserted = 0;
+ b->ignore_count = args->ignore_count;
+ b->disposition = args->tempflag ? disp_del : disp_donttouch;
+ if (args->condition != NULL)
+ {
+ b->cond_string = savestring (args->condition,
+ strlen (args->condition));
+ }
+ b->thread = args->thread;
+ mention (b);
+ return GDB_RC_OK;
+ }
+ else
+ return GDB_RC_NONE;
+ }
+ else if (!sals.nelts)
return GDB_RC_NONE;

/* Create a chain of things at always need to be cleaned up. */
@@ -4941,8 +4917,7 @@
cond_string = xcalloc (sals.nelts, sizeof (char **));
make_cleanup (xfree, cond_string);

- /* ----------------------------- SNIP -----------------------------
- Anything added to the cleanup chain beyond this point is assumed
+ /* Anything added to the cleanup chain beyond this point is assumed
to be part of a breakpoint. If the breakpoint create goes
through then that memory is not cleaned up. */
breakpoint_chain = make_cleanup (null_cleanup, 0);
@@ -4959,12 +4934,53 @@
/* Wait until now before checking for garbage at the end of the
address. That way cleanups can take care of freeing any
memory. */
- if (*address_end != '\0')
- error ("Garbage %s following breakpoint address", address_end);
+ {
+ char *tok = address_end;
+ while (tok && *tok)
+ {
+ char *end_tok;
+ int toklen;
+ char *cond_start = NULL;
+ char *cond_end = NULL;
+ while (*tok == ' ' || *tok == '\t')
+ tok++;

+ end_tok = tok;
+
+ while (*end_tok != ' ' && *end_tok != '\t' && *end_tok != '\000')
+ end_tok++;
+
+ toklen = end_tok - tok;
+
+ if (toklen >= 1 && strncmp (tok, "if", toklen) == 0)
+ {
+ tok = cond_start = end_tok + 1;
+ cond_end = cond_start;
+ while (*cond_end != '\000')
+ cond_end++;
+ args->condition = tok;
+ tok = cond_end;
+ }
+ else if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
+ {
+ char *tmptok;
+
+ tok = end_tok + 1;
+ tmptok = tok;
+ args->thread = strtol (tok, &tok, 0);
+ if (tok == tmptok)
+ error ("Junk after thread keyword.");
+ if (! valid_thread_id (args->thread))
+ error ("Unknown thread %d\n", args->thread);
+ }
+ else
+ error ("Junk at end of arguments.");
+ }
+ }
+
/* Resolve all line numbers to PC's. */
breakpoint_sals_to_pc (&sals, args->address);
-
+
/* Verify that conditions can be parsed, before setting any
breakpoints. */
for (i = 0; i < sals.nelts; i++)
@@ -4982,20 +4998,76 @@

create_breakpoints (sals, addr_string, cond, cond_string,
args->hardwareflag ? bp_hardware_breakpoint : bp_breakpoint,
- args->tempflag ? disp_del : disp_donttouch,
- args->thread, args->ignore_count, 0/*from-tty*/);
+ args->tempflag ? disp_del : disp_donttouch, orig_flags,
+ args->thread, args->ignore_count, args->from_tty);

+ if (args->from_tty && (sals.nelts > 1))
+ {
+ warning ("Multiple breakpoints were set.");
+ warning ("Use the \"delete\" command to delete unwanted breakpoints.");
+ }
+
/* That's it. Discard the cleanups for data inserted into the
breakpoint. */
discard_cleanups (breakpoint_chain);
+
/* But cleanup everything else. */
do_cleanups (old_chain);
+
return GDB_RC_OK;
+}
+
+static void break_command_1 (char *arg, int flags, int from_tty)
+{
+ struct captured_breakpoint_args args;
+ args.address = arg;
+ args.condition = NULL;
+ args.hardwareflag = flags & BP_HARDWAREFLAG;
+ args.tempflag = flags & BP_TEMPFLAG;
+ args.futureflag = flags & BP_FUTUREFLAG;
+ args.thread = -1;
+ args.ignore_count = 0;
+ args.from_tty = from_tty;
+
+ if (args.futureflag)
+ {
+ if (args.tempflag)
+ error ("Future breakpoints may not be specified as temporary.");
+ if (args.hardwareflag)
+ error ("Future breakpoints may not be specified as hardware.");
+ }
+
+ if (args.futureflag)
+ {
+ enum gdb_rc ret = catch_errors (do_captured_breakpoint,
+ &args, NULL, RETURN_MASK_ALL);
+ if (ret != GDB_RC_OK)
+ {
+ struct symtab_and_line sal =
+ {0, 0};
+ struct breakpoint *b = set_raw_breakpoint (sal, bp_breakpoint);
+
+ printf_unfiltered
+ ("Will attempt to resolve \"%s\" on future dynamic loads.\n", args.address);
+
+ b->number = ++breakpoint_count;
+ b->addr_string = savestring (args.address, strlen (args.address));
+ b->enable_state = bp_shlib_disabled;
+ b->inserted = 0;
+ b->disposition = disp_donttouch;
+ if (modify_breakpoint_hook)
+ modify_breakpoint_hook (b);
+ }
+ }
+ else
+ {
+ do_captured_breakpoint (&args);
+ }
}

enum gdb_rc
gdb_breakpoint (char *address, char *condition,
- int hardwareflag, int tempflag,
+ int hardwareflag, int tempflag, int futureflag,
int thread, int ignore_count)
{
struct captured_breakpoint_args args;
@@ -5003,6 +5075,7 @@
args.condition = condition;
args.hardwareflag = hardwareflag;
args.tempflag = tempflag;
+ args.futureflag = futureflag;
args.thread = thread;
args.ignore_count = ignore_count;
return catch_errors (do_captured_breakpoint, &args,
@@ -5223,6 +5296,12 @@
}

void
+future_break_command (char *arg, int from_tty)
+{
+ break_command_1 (arg, BP_FUTUREFLAG, from_tty);
+}
+
+void
break_command (char *arg, int from_tty)
{
break_command_1 (arg, 0, from_tty);
@@ -5446,6 +5525,7 @@

/* Now set up the breakpoint. */
b = set_raw_breakpoint (sal, bp_type);
+ b->original_flags = accessflag;
set_breakpoint_count (breakpoint_count + 1);
b->number = breakpoint_count;
b->disposition = disp_donttouch;
@@ -7187,10 +7267,27 @@
return 0;
}

-/* Re-set all breakpoints after symbols have been re-loaded. */
+unsigned int symbol_generation = 1;
+unsigned int breakpoint_generation = 0;
+
+void breakpoint_update ()
+{
+ if (breakpoint_generation != symbol_generation) {
+ breakpoint_re_set_all ();
+ breakpoint_generation = symbol_generation;
+ }
+}
+
void
breakpoint_re_set (void)
{
+ symbol_generation++;
+}
+
+/* Re-set all breakpoints after symbols have been re-loaded. */
+void
+breakpoint_re_set_all (void)
+{
struct breakpoint *b, *temp;
enum language save_language;
int save_input_radix;
@@ -7570,6 +7667,241 @@
{
map_breakpoint_numbers (args, enable_delete_breakpoint);
}
+
+/* Generate a break, watch, or catch command defined by B to the STREAM.
+
+ General worse case example,
+
+ break thread <t> if <expr>
+ commands
+ <command lines...>
+ end
+ ignore $bpnum <count>
+ disable $bpnum
+
+ In addition "set input-radix <r>" may precede the above sequence but
+ that is generated by save_breakpoints_command() which is this
+ function's caller. */
+
+static void
+write_one_breakpoint (struct breakpoint *b, struct ui_file *stream, struct ui_out *uiout)
+{
+ register struct command_line *l;
+
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ case bp_hardware_watchpoint:
+ case bp_read_watchpoint:
+ case bp_access_watchpoint:
+ switch (b->original_flags)
+ {
+ case hw_read:
+ fprintf_unfiltered (stream, "rwatch %s", b->exp_string);
+ break;
+ case hw_access:
+ fprintf_unfiltered (stream, "awatch %s", b->exp_string);
+ break;
+ case hw_execute:
+ internal_error (__FILE__, __LINE__, "execute watchpoints unsupported");
+ break;
+ case hw_write:
+ default:
+ fprintf_unfiltered (stream, "watch %s", b->exp_string);
+ break;
+ }
+
+ case bp_catch_load:
+ case bp_catch_unload:
+ fprintf_unfiltered (stream, "%scatch %sload", b->disposition == disp_del ? "t" : "",
+ b->type == bp_catch_unload ? "un" : "");
+ if (b->dll_pathname != NULL)
+ fputs_unfiltered (b->dll_pathname, stream);
+ break;
+
+ case bp_catch_fork:
+ fprintf_unfiltered (stream, "%scatch fork", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_vfork:
+ fprintf_unfiltered (stream, "%scatch vfork", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_exec:
+ fprintf_unfiltered (stream, "%scatch exec", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_catch:
+ fprintf_unfiltered (stream, "%scatch catch", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_catch_throw:
+ fprintf_unfiltered (stream, "%scatch throw", b->disposition == disp_del ? "t" : "");
+ break;
+
+ case bp_breakpoint:
+ case bp_hardware_breakpoint:
+ {
+ char *hardwareflag, *futureflag, *tempflag;
+
+ hardwareflag = (b->type == bp_hardware_breakpoint) ? "h" : "";
+ futureflag = ((b->enable_state == bp_shlib_disabled) ||
+ (b->original_flags & BP_FUTUREFLAG)) ? "future-" : "";
+ tempflag = (b->disposition == disp_del) ? "t" : "";
+
+ fprintf_unfiltered (stream, "%s%s%sbreak", futureflag, tempflag, hardwareflag);
+
+ if (b->addr_string)
+ {
+ int len = strlen(b->addr_string) - 1;
+ if (b->addr_string[len] == ' ')
+ b->addr_string[len] = 0;
+ else
+ len = 0;
+ fprintf_unfiltered (stream, " %s", b->addr_string);
+ if (len)
+ b->addr_string[len] = ' ';
+ }
+ else if (b->source_file)
+ fprintf_unfiltered (stream, " %s:%d", b->source_file, b->line_number);
+ else
+ fprintf_unfiltered(stream, " %s",
+ local_hex_string_custom((unsigned long) b->address, "08l"));
+ }
+ break;
+
+ default:
+ internal_error (__FILE__, __LINE__, "unhandled switch case");
+ break;
+ }
+
+ if (b->thread != -1)
+ fprintf_unfiltered (stream, " thread %d", b->thread);
+
+ if (b->cond_string)
+ fprintf_unfiltered (stream, " if %s", b->cond_string);
+
+ fputc_unfiltered ('\n', stream);
+
+ if ((l = b->commands))
+ {
+ fputs_unfiltered ("commands\n", stream);
+ print_command_lines (uiout, l, 4);
+ fputs_unfiltered ("end\n", stream);
+ }
+
+ if (b->ignore_count)
+ fprintf_unfiltered (stream, "ignore $bpnum %d\n", b->ignore_count);
+
+ if (b->enable_state == bp_disabled)
+ fputs_unfiltered ("disable $bpnum\n", stream);
+}
+
+static void
+save_breakpoints_command (char *arg, int from_tty)
+{
+ struct cleanup *cleanups;
+ register struct breakpoint *b;
+ int found_a_breakpoint = 0;
+ int current_radix = -1;
+ int skip;
+ struct ui_file *stream = NULL;
+ struct ui_out *uiout = NULL;
+ time_t t;
+ char **argv;
+ char *pathname, buf[256];
+
+ dont_repeat ();
+
+ if (arg == NULL)
+ {
+ error ("Arguments missing: file name in which to save breakpoint commands");
+ }
+ else if ((argv = buildargv (arg)) == NULL)
+ {
+ nomem (0);
+ }
+ cleanups = make_cleanup_freeargv (argv);
+
+ pathname = tilde_expand (arg);
+ make_cleanup (xfree, pathname);
+
+ ALL_BREAKPOINTS (b)
+ {
+ /* Filter out non-user breakpoints. */
+ if (b->type != bp_breakpoint
+ && b->type != bp_catch_load
+ && b->type != bp_catch_unload
+ && b->type != bp_catch_fork
+ && b->type != bp_catch_vfork
+ && b->type != bp_catch_exec
+ && b->type != bp_catch_catch
+ && b->type != bp_catch_throw
+ && b->type != bp_hardware_breakpoint
+ && b->type != bp_watchpoint
+ && b->type != bp_read_watchpoint
+ && b->type != bp_access_watchpoint
+ && b->type != bp_hardware_watchpoint)
+ continue;
+
+ if (! found_a_breakpoint++)
+ {
+ stream = gdb_fopen (pathname, FOPEN_WT);
+ if (stream == NULL)
+ error ("Unable to open file '%s' for saving breakpoints (%s)",
+ arg, strerror (errno));
+ make_cleanup_ui_file_delete (stream);
+ uiout = cli_out_new (stream);
+ if (uiout == NULL)
+ error ("Unable to create cli_out from file for saving breakpoints");
+ make_cleanup_ui_out_delete (uiout);
+ if (time (&t) != -1)
+ {
+ char *l = setlocale (LC_ALL, NULL);
+ if (l)
+ {
+ char *orig_locale = strcpy (xmalloc (strlen (l) + 1), l);
+ setlocale (LC_ALL, "");
+ if (strftime (buf, sizeof (buf), "%a %b %e %H:%M:%S %Z %Y", localtime (&t)))
+ fprintf_unfiltered (stream, "# Saved breakpoints file created on %s\n\n", buf);
+ setlocale (LC_ALL, orig_locale);
+ }
+ }
+ fprintf_unfiltered (stream, "set $current_radix = $input_radix\n"
+ "set input-radix 012\n\n");
+ current_radix = 10;
+ }
+
+ skip = (b->commands || b->ignore_count || b->enable_state == bp_disabled);
+ if (skip)
+ fputc_unfiltered ('\n', stream);
+
+ if (b->input_radix != current_radix)
+ {
+ current_radix = b->input_radix;
+ fprintf_unfiltered (stream, "set input-radix 0%o\n", current_radix);
+ }
+
+ write_one_breakpoint (b, stream, uiout);
+
+ if (skip && b->next)
+ fputc_unfiltered ('\n', stream);
+ }
+
+ if (! found_a_breakpoint)
+ printf_filtered ("No breakpoints or watchpoints to save.\n");
+ else
+ {
+ fputs_unfiltered ("\n", stream);
+ if (current_radix != 10)
+ fputs_unfiltered ("set input-radix 012\n", stream);
+ fputs_unfiltered ("set input-radix $current_radix\n", stream);
+ if (from_tty)
+ printf_filtered ("Breakpoints saved to file '%s'.\n", arg);
+ }
+
+ do_cleanups (cleanups);
+}
\f
/* Use default_breakpoint_'s, or nothing if they aren't valid. */

@@ -7757,6 +8089,9 @@
\n\
Multiple breakpoints at one place are permitted, and useful if conditional.\n\
\n\
+break ... if <cond> sets condition <cond> on the breakpoint as it is created.\n\
+braek ... [ if <cond> ] thread <n> makes the breakpoint specific to thread <n>.\n\
+\n\
Do \"help breakpoints\" for info on other commands dealing with breakpoints.", NULL));
c->completer = location_completer;

@@ -7765,6 +8100,11 @@
add_com_alias ("bre", "break", class_run, 1);
add_com_alias ("brea", "break", class_run, 1);

+ add_com ("future-break", class_breakpoint, future_break_command,
+ "Set breakpoint at expression. If it can't be done now, attempt it\n"
+ "again each time code is dynamically loaded.");
+ add_com_alias ("fb", "future-break", class_breakpoint, 2);
+
add_com ("xbreak", class_breakpoint, break_at_finish_command,
concat ("Set breakpoint at procedure exit. \n\
Argument may be function name, or \"*\" and an address.\n\
@@ -7936,6 +8276,12 @@
hardware.)",
&setlist);
add_show_from_set (c, &showlist);
+
+ c = add_cmd ("save-breakpoints", class_breakpoint, save_breakpoints_command,
+ "Save current breakpoint definitions as a script.\n\
+Use the -command option or 'source' command in another debug\n\
+'session to restore them.", &cmdlist);
+ c->completer = filename_completer;

can_use_hw_watchpoints = 1;
}
Index: breakpoint.h
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.h,v
retrieving revision 1.10
diff -u -r1.10 breakpoint.h
--- breakpoint.h 2001/10/20 23:54:29 1.10
+++ breakpoint.h 2001/12/11 08:17:40
@@ -297,6 +297,9 @@
char *exec_pathname;

asection *section;
+
+ /* used for save-breakpoints. */
+ int original_flags;
};
\f
/* The following stuff is an abstract data type "bpstat" ("breakpoint
@@ -515,6 +518,8 @@

/* Forward declarations for prototypes */
struct frame_info;
+
+extern void set_breakpoint_count (int);

extern enum breakpoint_here breakpoint_here_p (CORE_ADDR);

Index: defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.66
diff -u -r1.66 defs.h
--- defs.h 2001/12/02 02:57:13 1.66
+++ defs.h 2001/12/11 08:17:42
@@ -544,6 +544,9 @@
struct ui_file;
extern struct cleanup *make_cleanup_ui_file_delete (struct ui_file *);

+struct ui_out;
+extern struct cleanup *make_cleanup_ui_out_delete (struct ui_out *);
+
extern struct cleanup *make_cleanup_close (int fd);

extern struct cleanup *make_cleanup_bfd_close (bfd *abfd);
Index: gdb.h
===================================================================
RCS file: /cvs/src/src/gdb/gdb.h,v
retrieving revision 1.2
diff -u -r1.2 gdb.h
--- gdb.h 2001/09/18 05:00:49 1.2
+++ gdb.h 2001/12/11 08:17:42
@@ -48,7 +48,7 @@

/* Create a breakpoint at ADDRESS (a GDB source and line). */
enum gdb_rc gdb_breakpoint (char *address, char *condition,
- int hardwareflag, int tempflag,
+ int hardwareflag, int tempflag, int futureflag,
int thread, int ignore_count);

/* Switch thread and print notification. */
Index: main.c
===================================================================
RCS file: /cvs/src/src/gdb/main.c,v
retrieving revision 1.14
diff -u -r1.14 main.c
--- main.c 2001/11/22 00:23:12 1.14
+++ main.c 2001/12/11 08:17:43
@@ -616,6 +616,17 @@
catch_command_errors (source_command, gdbinit, 0, RETURN_MASK_ALL);
}

+ /* These need to be set this late in the initialization to ensure that
+ they are defined for the current environment. They define the
+ radix variables needed by a save-breakpoints file to preserve the
+ radix across the breakpoints restoration assuming they are restored
+ using the -x (-command) command line options. */
+
+ set_internalvar (lookup_internalvar ("input_radix"),
+ value_from_longest (builtin_type_int, (LONGEST) input_radix));
+ set_internalvar (lookup_internalvar ("output_radix"),
+ value_from_longest (builtin_type_int, (LONGEST) output_radix));
+
for (i = 0; i < ncmd; i++)
{
#if 0
Index: tracepoint.c
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.c,v
retrieving revision 1.28
diff -u -r1.28 tracepoint.c
--- tracepoint.c 2001/11/06 23:38:15 1.28
+++ tracepoint.c 2001/12/11 08:17:46
@@ -2262,7 +2262,7 @@
struct action_line *line;
FILE *fp;
char *i1 = " ", *i2 = " ";
- char *indent, *actionline;
+ char *indent, *actionline, *pathname;
char tmp[40];

if (args == 0 || *args == 0)
@@ -2274,9 +2274,12 @@
return;
}

- if (!(fp = fopen (args, "w")))
- error ("Unable to open file '%s' for saving tracepoints");
-
+ pathname = tilde_expand (args);
+ if (!(fp = fopen (pathname, "w")))
+ error ("Unable to open file '%s' for saving tracepoints (%s)",
+ args, strerror (errno));
+ xfree (pathname);
+
ALL_TRACEPOINTS (tp)
{
if (tp->addr_string)
Index: ui-out.c
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.c,v
retrieving revision 1.18
diff -u -r1.18 ui-out.c
--- ui-out.c 2001/07/06 03:53:11 1.18
+++ ui-out.c 2001/12/11 08:17:47
@@ -1117,6 +1117,14 @@
return uiout;
}

+void
+ui_out_delete (struct ui_out *uiout)
+{
+ if (uiout->data != NULL)
+ xfree (uiout->data);
+ xfree (uiout);
+}
+
/* standard gdb initialization hook */

void
Index: ui-out.h
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.h,v
retrieving revision 1.15
diff -u -r1.15 ui-out.h
--- ui-out.h 2001/07/06 03:53:11 1.15
+++ ui-out.h 2001/12/11 08:17:47
@@ -272,4 +272,6 @@
struct ui_out_data *data,
int flags);

+extern void ui_out_delete (struct ui_out *uiout);
+
#endif /* UI_OUT_H */
Index: utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.51
diff -u -r1.51 utils.c
--- utils.c 2001/11/15 18:35:05 1.51
+++ utils.c 2001/12/11 08:17:49
@@ -254,6 +254,18 @@
return make_my_cleanup (&cleanup_chain, do_ui_file_delete, arg);
}

+static void
+do_ui_out_delete (void *arg)
+{
+ ui_out_delete (arg);
+}
+
+struct cleanup *
+make_cleanup_ui_out_delete (struct ui_out *arg)
+{
+ return make_my_cleanup (&cleanup_chain, do_ui_out_delete, arg);
+}
+
struct cleanup *
make_my_cleanup (struct cleanup **pmy_chain, make_cleanup_ftype *function,
void *arg)
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.59
diff -u -r1.59 gdb.texinfo
--- gdb.texinfo 2001/11/30 23:03:09 1.59
+++ gdb.texinfo 2001/12/11 08:18:09
@@ -2335,6 +2335,7 @@
* Set Catchpoints:: Setting catchpoints
* Delete Breaks:: Deleting breakpoints
* Disabling:: Disabling breakpoints
+* Saving:: Saving breakpoints
* Conditions:: Break conditions
* Break Commands:: Breakpoint command lists
* Breakpoint Menus:: Breakpoint menus
@@ -2420,6 +2421,14 @@
above (or no argument) specifying where to break. @xref{Conditions,
,Break conditions}, for more information on breakpoint conditions.

+@kindex future-break
+@item future-break @var{args}
+Set a `future' breakpoint. @var{args} are the same as for the
+@code{hbreak} command and the breakpoint is set in the same way.
+However, if @value{GDBN} is unable to set the breakpoint when the
+command is executed, it will store the expression, and try again to set
+it after any new symbol files or shared libraries are loaded.
+
@kindex tbreak
@item tbreak @var{args}
Set a breakpoint enabled only for one stop. @var{args} are the
@@ -2941,6 +2950,22 @@
breakpoint of its own, but it does not change the state of your other
breakpoints; see @ref{Continuing and Stepping, ,Continuing and
stepping}.)
+
+@node Saving
+@subsection Saving breakpoints
+@cindex save breakpoints for future sessions
+
+Sometimes, it can be convenient to save the current set of breakpoints
+for use in a future debugging session:
+
+@table @code
+@kindex save-breakpoints
+@item save-breakpoints
+Save all current breakpoint definitions, together with their
+ignore-counts and command scripts, into the file @file{@var{filename}}.
+To read the saved breakpoint definitions, use the @code{source} command
+(@pxref{Command Files}).
+@end table

@node Conditions
@subsection Break conditions
Index: mi/mi-cmd-break.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-cmd-break.c,v
retrieving revision 1.6
diff -u -r1.6 mi-cmd-break.c
--- mi-cmd-break.c 2001/09/18 05:00:51 1.6
+++ mi-cmd-break.c 2001/12/11 08:18:12
@@ -60,15 +60,17 @@
{
REG_BP,
HW_BP,
+ FUT_BP,
REGEXP_BP
};

/* Insert a breakpoint. The type of breakpoint is specified by the
- first argument: -break-insert <location> --> insert a regular
- breakpoint. -break-insert -t <location> --> insert a temporary
- breakpoint. -break-insert -h <location> --> insert an hardware
- breakpoint. -break-insert -t -h <location> --> insert a temporary
- hw bp.
+ first argument:
+ -break-insert <location> --> insert a regular breakpoint.
+ -break-insert -t <location> --> insert a temporary breakpoint.
+ -break-insert -h <location> --> insert an hardware breakpoint.
+ -break-insert -t -h <location> --> insert a temporary hw bp.
+ -break-insert -f <location> --> insert a future breakpoint.
-break-insert -r <regexp> --> insert a bp at functions matching
<regexp> */

@@ -85,13 +87,14 @@
struct gdb_events *old_hooks;
enum opt
{
- HARDWARE_OPT, TEMP_OPT /*, REGEXP_OPT */ , CONDITION_OPT,
+ HARDWARE_OPT, TEMP_OPT, FUTURE_OPT /*, REGEXP_OPT */ , CONDITION_OPT,
IGNORE_COUNT_OPT, THREAD_OPT
};
static struct mi_opt opts[] =
{
{"h", HARDWARE_OPT, 0},
{"t", TEMP_OPT, 0},
+ {"f", FUTURE_OPT, 0},
{"c", CONDITION_OPT, 1},
{"i", IGNORE_COUNT_OPT, 1},
{"p", THREAD_OPT, 1},
@@ -115,6 +118,9 @@
case HARDWARE_OPT:
type = HW_BP;
break;
+ case FUTURE_OPT:
+ type = FUT_BP;
+ break;
#if 0
case REGEXP_OPT:
type = REGEXP_BP;
@@ -143,13 +149,12 @@
switch (type)
{
case REG_BP:
- rc = gdb_breakpoint (address, condition,
- 0 /*hardwareflag */ , temp_p,
- thread, ignore_count);
- break;
case HW_BP:
+ case FUT_BP:
rc = gdb_breakpoint (address, condition,
- 1 /*hardwareflag */ , temp_p,
+ (type == REG_BP) ? 0 : 1,
+ temp_p,
+ (type == FUT_BP) ? 0 : 1,
thread, ignore_count);
break;
#if 0