From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 31599 invoked by alias); 11 Dec 2001 08:28:00 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 29877 invoked from network); 11 Dec 2001 08:26:36 -0000 Received: from unknown (HELO dc-mx04.cluster1.charter.net) (209.225.8.14) by sources.redhat.com with SMTP; 11 Dec 2001 08:26:36 -0000 Received: from [24.241.47.50] (HELO localhost) by dc-mx04.cluster1.charter.net (CommuniGate Pro SMTP 3.4.6) with ESMTP id 5709787; Tue, 11 Dec 2001 03:32:52 -0500 Received: from localhost ([127.0.0.1] helo=salmon.localnet) by localhost with esmtp (Exim 3.33 #1 (Debian)) id 16DiFC-0000iD-00; Tue, 11 Dec 2001 03:26:30 -0500 From: Klee Dienes Reply-To: Klee Dienes To: Michael Snyder cc: Klee Dienes , gdb-patches@sources.redhat.com Subject: Re: [RFC] add 'save-breakpoints' command In-Reply-To: Your message of "Tue, 04 Dec 2001 14:45:32 PST." <3C0D520C.E9016E83@cygnus.com> References: <3C0D520C.E9016E83@cygnus.com> Date: Tue, 11 Dec 2001 00:28:00 -0000 Message-Id: X-SW-Source: 2001-12/txt/msg00296.txt.bz2 > 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 Ira L. Ruben James Ingham * 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 if ' 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 if " and "break thread " 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 thread if ' --- 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 +#include #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 if + commands + + end + ignore $bpnum + disable $bpnum + + In addition "set input-radix " 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); +} /* 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 sets condition on the breakpoint as it is created.\n\ +braek ... [ if ] thread makes the breakpoint specific to thread .\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; }; /* 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 --> insert a regular - breakpoint. -break-insert -t --> insert a temporary - breakpoint. -break-insert -h --> insert an hardware - breakpoint. -break-insert -t -h --> insert a temporary - hw bp. + first argument: + -break-insert --> insert a regular breakpoint. + -break-insert -t --> insert a temporary breakpoint. + -break-insert -h --> insert an hardware breakpoint. + -break-insert -t -h --> insert a temporary hw bp. + -break-insert -f --> insert a future breakpoint. -break-insert -r --> insert a bp at functions matching */ @@ -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