This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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



> 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 <address> 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);
+}
 
 /* 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;
   };
 
 /* 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]