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]

RFC: First stab at breakpoints in multiple locations


This patch (which, obviously, I'm not proposing to commit) is an initial
prototype of the one breakpoint, many locations idea I've discussed before.

It handles only the case of breakpoints by line number, not by symbol name
or any other method; and it does not handle those particularly well.  It
will dumbly set a breakpoint at the beginning of every location the line
table says represents $file:$line.  This includes inlined copies and
"fragments" resulting from optimization or otherwise compiler-generated; one
test fails because we hit a breakpoint on a "for(;;) at both the beginning
and end of the loop.  We would need a heuristic to control this behavior,
in the absence of real debug information describing it (see the stmt_p
register in .debug_line; no compiler that I know of sets it properly).

It's got a couple of gross hacks in it, and has at least one potential
memory clobber in breakpoint.c.  But it works well enough for the testsuite,
and the included new test, if you want to play with it.

I didn't handle any of the thorny issues, most of which relate to interface.
For instance:
  - "break overloadedFn" now sets one breakpoint, on all of the overloaded
    copies, instead of one breakpoint on each.
  - "info breakpoints" will only show one address.
  - The breakpoint-hit status message won't tell you which copy you've
    hit, so you can keep continuing and seeing "Breakpoint 14" over and
    over again when you're actually at different PCs.
  - MI only shows one address.
  - There's now one breakpoint condition for the whole set, which means
    the expression has to evaluate in multiple contexts; if it doesn't
    parse in some of them, you lose.

And so on.  I have a list somewhere of other issues that I can't find at the
moment.  But this is in a vaguely usable state, so I thought I would publish
it in case anyone wanted to comment.  This is just one of the many possible
solutions to the problem, but I think it has a certain elegance... if folks
like it, the next thing to do would be to support other types of
breakpoints, and then to tackle the interface issues.  I still have a pile
of discussion of these issues from 2003 to re-read.

-- 
Daniel Jacobowitz
CodeSourcery, LLC

Index: src/gdb/hppa-hpux-tdep.c
===================================================================
--- src.orig/gdb/hppa-hpux-tdep.c	2005-03-13 14:38:40.000000000 -0500
+++ src/gdb/hppa-hpux-tdep.c	2005-03-13 14:48:09.000000000 -0500
@@ -733,7 +733,6 @@ cover_find_stub_with_shl_get (void *args
 static int
 initialize_hp_cxx_exception_support (void)
 {
-  struct symtabs_and_lines sals;
   struct cleanup *old_chain;
   struct cleanup *canonical_strings_chain = NULL;
   int i;
Index: src/gdb/tui/tui-winsource.c
===================================================================
--- src.orig/gdb/tui/tui-winsource.c	2005-03-13 14:38:09.000000000 -0500
+++ src/gdb/tui/tui-winsource.c	2005-03-13 14:48:09.000000000 -0500
@@ -419,7 +419,7 @@ tui_update_breakpoint_info (struct tui_w
                && (strcmp (src->filename, bp->source_file) == 0)
                && bp->line_number == line->line_or_addr.line_no)
               || (win == TUI_DISASM_WIN
-                  && bp->loc->address == line->line_or_addr.addr))
+                  && breakpoint_at_address_p (bp, line->line_or_addr.addr)))
             {
               if (bp->enable_state == bp_disabled)
                 mode |= TUI_BP_DISABLED;
Index: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c	2005-03-13 14:47:02.000000000 -0500
+++ src/gdb/breakpoint.c	2005-03-13 18:42:55.000000000 -0500
@@ -55,6 +55,82 @@
 
 #include "gdb-events.h"
 
+/* GDB maintains two types of information about each breakpoint (or
+   watchpoint, or other related event).  The first type corresponds
+   to struct breakpoint; this is a relatively high-level structure
+   which contains the source location(s), stopping conditions, user
+   commands to execute when the breakpoint is hit, and so forth.
+
+   The second type of information corresponds to struct bp_location.
+   Each breakpoint has one or (eventually) more locations associated
+   with it, which represent target-specific and machine-specific
+   mechanisms for stopping the program.  For instance, a watchpoint
+   expression may require multiple hardware watchpoints in order to
+   catch all changes in the value of the expression being watched.  */
+
+enum bp_loc_type
+{
+  bp_loc_software_breakpoint,
+  bp_loc_hardware_breakpoint,
+  bp_loc_hardware_watchpoint,
+  bp_loc_other			/* Miscellaneous...  */
+};
+
+struct bp_location
+{
+  /* Chain pointer to the next breakpoint location.  */
+  struct bp_location *next;
+
+  /* Type of this breakpoint location.  */
+  enum bp_loc_type loc_type;
+
+  /* Each breakpoint location must belong to exactly one higher-level
+     breakpoint.  This and the DUPLICATE flag are more straightforward
+     than reference counting.  */
+  struct breakpoint *owner;
+
+  /* Nonzero if this breakpoint is now inserted.  */
+  char inserted;
+
+  /* Nonzero if this is not the first breakpoint in the list
+     for the given address.  */
+  char duplicate;
+
+  /* If we someday support real thread-specific breakpoints, then
+     the breakpoint location will need a thread identifier.  */
+
+  /* Data for specific breakpoint types.  These could be a union, but
+     simplicity is more important than memory usage for breakpoints.  */
+
+  /* Note that zero is a perfectly valid code address on some platforms
+     (for example, the mn10200 (OBSOLETE) and mn10300 simulators).  NULL
+     is not a special value for this field.  Valid for all types except
+     bp_loc_other.  */
+  CORE_ADDR address;
+
+  /* For any breakpoint type with an address, this is the BFD section
+     associated with the address.  Used primarily for overlay debugging.  */
+  asection *section;
+
+  /* "Real" contents of byte where breakpoint has been inserted.
+     Valid only when breakpoints are in the program.  Under the complete
+     control of the target insert_breakpoint and remove_breakpoint routines.
+     No other code should assume anything about the value(s) here.
+     Valid only for bp_loc_software_breakpoint.  */
+  char shadow_contents[BREAKPOINT_MAX];
+
+  /* Address at which breakpoint was requested, either by the user or
+     by GDB for internal breakpoints.  This will usually be the same
+     as ``address'' (above) except for cases in which
+     ADJUST_BREAKPOINT_ADDRESS has computed a different address at
+     which to place the breakpoint in order to comply with a
+     processor's architectual constraints.  */
+  CORE_ADDR requested_address;
+
+  /* The next location associated with OWNER.  */
+  struct bp_location *loc_next;
+};
+
 /* Prototypes for local functions. */
 
 static void until_break_command_continuation (struct continuation_arg *arg);
@@ -106,7 +182,7 @@ static void breakpoints_info (char *, in
 
 static void breakpoint_1 (int, int);
 
-static bpstat bpstat_alloc (struct breakpoint *, bpstat);
+static bpstat bpstat_alloc (struct bp_location *, bpstat);
 
 static int breakpoint_cond_eval (void *);
 
@@ -259,6 +335,16 @@ static int overlay_events_enabled;
 	     B ? (TMP=B->next, 1): 0;	\
 	     B = TMP)
 
+/* Similar iterators within one high-level breakpoint.  */
+
+#define ALL_ONE_BP_LOCATIONS(OWNER,B)	\
+	for ((B) = (OWNER)->loc; (B); (B) = (B)->loc_next)
+
+#define ALL_ONE_BP_LOCATIONS_SAFE(OWNER,B,TMP)		\
+	for ((B) = (OWNER)->loc;			\
+	     (B) ? ((TMP) = (B)->loc_next, 1) : 0;	\
+	     (B) = (TMP))
+
 /* True if breakpoint hit counts should be displayed in breakpoint info.  */
 
 int show_breakpoint_hit_counts = 1;
@@ -1907,7 +1993,8 @@ bpstat_find_breakpoint (bpstat bsp, stru
 
   for (; bsp != NULL; bsp = bsp->next)
     {
-      if (bsp->breakpoint_at == breakpoint)
+      if (bsp->breakpoint_at != NULL
+	  && bsp->breakpoint_at->owner == breakpoint)
 	return bsp;
     }
   return NULL;
@@ -1932,11 +2019,11 @@ bpstat_find_step_resume_breakpoint (bpst
 
   for (; bsp != NULL; bsp = bsp->next)
     {
-      if ((bsp->breakpoint_at != NULL) &&
-	  (bsp->breakpoint_at->type == bp_step_resume) &&
-	  (bsp->breakpoint_at->thread == current_thread || 
-	   bsp->breakpoint_at->thread == -1))
-	return bsp->breakpoint_at;
+      if (bsp->breakpoint_at != NULL
+	  && bsp->breakpoint_at->owner->type == bp_step_resume
+	  && (bsp->breakpoint_at->owner->thread == current_thread
+	      || bsp->breakpoint_at->owner->thread == -1))
+	return bsp->breakpoint_at->owner;
     }
 
   internal_error (__FILE__, __LINE__, _("No step_resume breakpoint found."));
@@ -1952,7 +2039,7 @@ bpstat_find_step_resume_breakpoint (bpst
 int
 bpstat_num (bpstat *bsp)
 {
-  struct breakpoint *b;
+  struct bp_location *b;
 
   if ((*bsp) == NULL)
     return 0;			/* No more breakpoint values */
@@ -1963,7 +2050,7 @@ bpstat_num (bpstat *bsp)
       if (b == NULL)
 	return -1;		/* breakpoint that's been deleted since */
       else
-	return b->number;	/* We have its number */
+	return b->owner->number; /* We have its number */
     }
 }
 
@@ -2090,6 +2177,8 @@ print_it_typical (bpstat bs)
 {
   struct cleanup *old_chain, *ui_out_chain;
   struct ui_stream *stb;
+  struct breakpoint *bpt;
+
   stb = ui_out_stream_new (uiout);
   old_chain = make_cleanup_ui_out_stream_delete (stb);
   /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
@@ -2097,19 +2186,20 @@ print_it_typical (bpstat bs)
   if (bs->breakpoint_at == NULL)
     return PRINT_UNKNOWN;
 
-  switch (bs->breakpoint_at->type)
+  bpt = bs->breakpoint_at->owner;
+  switch (bpt->type)
     {
     case bp_breakpoint:
     case bp_hardware_breakpoint:
-      if (bs->breakpoint_at->loc->address != bs->breakpoint_at->loc->requested_address)
-	breakpoint_adjustment_warning (bs->breakpoint_at->loc->requested_address,
-	                               bs->breakpoint_at->loc->address,
-				       bs->breakpoint_at->number, 1);
-      annotate_breakpoint (bs->breakpoint_at->number);
+      if (bs->breakpoint_at->address != bs->breakpoint_at->requested_address)
+	breakpoint_adjustment_warning (bs->breakpoint_at->requested_address,
+	                               bs->breakpoint_at->address,
+				       bpt->number, 1);
+      annotate_breakpoint (bpt->number);
       ui_out_text (uiout, "\nBreakpoint ");
       if (ui_out_is_mi_like_p (uiout))
 	ui_out_field_string (uiout, "reason", "breakpoint-hit");
-      ui_out_field_int (uiout, "bkptno", bs->breakpoint_at->number);
+      ui_out_field_int (uiout, "bkptno", bpt->number);
       ui_out_text (uiout, ", ");
       return PRINT_SRC_AND_LOC;
       break;
@@ -2136,42 +2226,42 @@ print_it_typical (bpstat bs)
       break;
 
     case bp_catch_load:
-      annotate_catchpoint (bs->breakpoint_at->number);
+      annotate_catchpoint (bpt->number);
       printf_filtered (_("\nCatchpoint %d (loaded %s), "),
-		       bs->breakpoint_at->number,
-		       bs->breakpoint_at->triggered_dll_pathname);
+		       bpt->number,
+		       bpt->triggered_dll_pathname);
       return PRINT_SRC_AND_LOC;
       break;
 
     case bp_catch_unload:
-      annotate_catchpoint (bs->breakpoint_at->number);
+      annotate_catchpoint (bpt->number);
       printf_filtered (_("\nCatchpoint %d (unloaded %s), "),
-		       bs->breakpoint_at->number,
-		       bs->breakpoint_at->triggered_dll_pathname);
+		       bpt->number,
+		       bpt->triggered_dll_pathname);
       return PRINT_SRC_AND_LOC;
       break;
 
     case bp_catch_fork:
-      annotate_catchpoint (bs->breakpoint_at->number);
+      annotate_catchpoint (bpt->number);
       printf_filtered (_("\nCatchpoint %d (forked process %d), "),
-		       bs->breakpoint_at->number, 
-		       bs->breakpoint_at->forked_inferior_pid);
+		       bpt->number, 
+		       bpt->forked_inferior_pid);
       return PRINT_SRC_AND_LOC;
       break;
 
     case bp_catch_vfork:
-      annotate_catchpoint (bs->breakpoint_at->number);
+      annotate_catchpoint (bpt->number);
       printf_filtered (_("\nCatchpoint %d (vforked process %d), "),
-		       bs->breakpoint_at->number, 
-		       bs->breakpoint_at->forked_inferior_pid);
+		       bpt->number, 
+		       bpt->forked_inferior_pid);
       return PRINT_SRC_AND_LOC;
       break;
 
     case bp_catch_exec:
-      annotate_catchpoint (bs->breakpoint_at->number);
+      annotate_catchpoint (bpt->number);
       printf_filtered (_("\nCatchpoint %d (exec'd %s), "),
-		       bs->breakpoint_at->number,
-		       bs->breakpoint_at->exec_pathname);
+		       bpt->number,
+		       bpt->exec_pathname);
       return PRINT_SRC_AND_LOC;
       break;
 
@@ -2179,9 +2269,9 @@ print_it_typical (bpstat bs)
       if (current_exception_event && 
 	  (CURRENT_EXCEPTION_KIND == EX_EVENT_CATCH))
 	{
-	  annotate_catchpoint (bs->breakpoint_at->number);
+	  annotate_catchpoint (bpt->number);
 	  printf_filtered (_("\nCatchpoint %d (exception caught), "), 
-			   bs->breakpoint_at->number);
+			   bpt->number);
 	  if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE)
 	    printf_filtered (_("throw location %s:%d, "),
 			     CURRENT_EXCEPTION_THROW_FILE,
@@ -2210,9 +2300,9 @@ print_it_typical (bpstat bs)
       if (current_exception_event && 
 	  (CURRENT_EXCEPTION_KIND == EX_EVENT_THROW))
 	{
-	  annotate_catchpoint (bs->breakpoint_at->number);
+	  annotate_catchpoint (bpt->number);
 	  printf_filtered (_("\nCatchpoint %d (exception thrown), "),
-			   bs->breakpoint_at->number);
+			   bpt->number);
 	  if (CURRENT_EXCEPTION_THROW_PC && CURRENT_EXCEPTION_THROW_LINE)
 	    printf_filtered (_("throw location %s:%d, "),
 			     CURRENT_EXCEPTION_THROW_FILE,
@@ -2241,16 +2331,16 @@ print_it_typical (bpstat bs)
     case bp_hardware_watchpoint:
       if (bs->old_val != NULL)
 	{
-	  annotate_watchpoint (bs->breakpoint_at->number);
+	  annotate_watchpoint (bpt->number);
 	  if (ui_out_is_mi_like_p (uiout))
 	    ui_out_field_string (uiout, "reason", "watchpoint-trigger");
-	  mention (bs->breakpoint_at);
+	  mention (bpt);
 	  ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value");
 	  ui_out_text (uiout, "\nOld value = ");
 	  value_print (bs->old_val, stb->stream, 0, Val_pretty_default);
 	  ui_out_field_stream (uiout, "old", stb);
 	  ui_out_text (uiout, "\nNew value = ");
-	  value_print (bs->breakpoint_at->val, stb->stream, 0, Val_pretty_default);
+	  value_print (bpt->val, stb->stream, 0, Val_pretty_default);
 	  ui_out_field_stream (uiout, "new", stb);
 	  do_cleanups (ui_out_chain);
 	  ui_out_text (uiout, "\n");
@@ -2264,10 +2354,10 @@ print_it_typical (bpstat bs)
     case bp_read_watchpoint:
       if (ui_out_is_mi_like_p (uiout))
 	ui_out_field_string (uiout, "reason", "read-watchpoint-trigger");
-      mention (bs->breakpoint_at);
+      mention (bpt);
       ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value");
       ui_out_text (uiout, "\nValue = ");
-      value_print (bs->breakpoint_at->val, stb->stream, 0, Val_pretty_default);
+      value_print (bpt->val, stb->stream, 0, Val_pretty_default);
       ui_out_field_stream (uiout, "value", stb);
       do_cleanups (ui_out_chain);
       ui_out_text (uiout, "\n");
@@ -2277,10 +2367,10 @@ print_it_typical (bpstat bs)
     case bp_access_watchpoint:
       if (bs->old_val != NULL)     
 	{
-	  annotate_watchpoint (bs->breakpoint_at->number);
+	  annotate_watchpoint (bpt->number);
 	  if (ui_out_is_mi_like_p (uiout))
 	    ui_out_field_string (uiout, "reason", "access-watchpoint-trigger");
-	  mention (bs->breakpoint_at);
+	  mention (bpt);
 	  ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value");
 	  ui_out_text (uiout, "\nOld value = ");
 	  value_print (bs->old_val, stb->stream, 0, Val_pretty_default);
@@ -2291,13 +2381,13 @@ print_it_typical (bpstat bs)
 	}
       else 
 	{
-	  mention (bs->breakpoint_at);
+	  mention (bpt);
 	  if (ui_out_is_mi_like_p (uiout))
 	    ui_out_field_string (uiout, "reason", "access-watchpoint-trigger");
 	  ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "value");
 	  ui_out_text (uiout, "\nValue = ");
 	}
-      value_print (bs->breakpoint_at->val, stb->stream, 0,Val_pretty_default);
+      value_print (bpt->val, stb->stream, 0,Val_pretty_default);
       ui_out_field_stream (uiout, "new", stb);
       do_cleanups (ui_out_chain);
       ui_out_text (uiout, "\n");
@@ -2356,9 +2446,9 @@ print_bp_stop_message (bpstat bs)
     case print_it_normal:
       /* Normal case.  Call the breakpoint's print_it method, or
 	 print_it_typical.  */
-      if (bs->breakpoint_at != NULL && bs->breakpoint_at->ops != NULL
-	  && bs->breakpoint_at->ops->print_it != NULL)
-	return bs->breakpoint_at->ops->print_it (bs->breakpoint_at);
+      if (bs->breakpoint_at != NULL && bs->breakpoint_at->owner->ops != NULL
+	  && bs->breakpoint_at->owner->ops->print_it != NULL)
+	return bs->breakpoint_at->owner->ops->print_it (bs->breakpoint_at->owner);
       else
 	return print_it_typical (bs);
       break;
@@ -2430,13 +2520,13 @@ breakpoint_cond_eval (void *exp)
 /* Allocate a new bpstat and chain it to the current one.  */
 
 static bpstat
-bpstat_alloc (struct breakpoint *b, bpstat cbs /* Current "bs" value */ )
+bpstat_alloc (struct bp_location *loc, bpstat cbs /* Current "bs" value */ )
 {
   bpstat bs;
 
   bs = (bpstat) xmalloc (sizeof (*bs));
   cbs->next = bs;
-  bs->breakpoint_at = b;
+  bs->breakpoint_at = loc;
   /* If the condition is false, etc., don't do the commands.  */
   bs->commands = NULL;
   bs->old_val = NULL;
@@ -2466,7 +2556,7 @@ watchpoint_check (void *p)
   struct frame_info *fr;
   int within_current_scope;
 
-  b = bs->breakpoint_at;
+  b = bs->breakpoint_at->owner;
 
   if (b->exp_valid_block == NULL)
     within_current_scope = 1;
@@ -2506,7 +2596,7 @@ watchpoint_check (void *p)
          we might be in the middle of evaluating a function call.  */
 
       struct value *mark = value_mark ();
-      struct value *new_val = evaluate_expression (bs->breakpoint_at->exp);
+      struct value *new_val = evaluate_expression (bs->breakpoint_at->owner->exp);
       if (!value_equal (b->val, new_val))
 	{
 	  release_value (new_val);
@@ -2541,7 +2631,7 @@ watchpoint_check (void *p)
       if (ui_out_is_mi_like_p (uiout))
 	ui_out_field_string (uiout, "reason", "watchpoint-scope");
       ui_out_text (uiout, "\nWatchpoint ");
-      ui_out_field_int (uiout, "wpnum", bs->breakpoint_at->number);
+      ui_out_field_int (uiout, "wpnum", bs->breakpoint_at->owner->number);
       ui_out_text (uiout, " deleted because the program has left the block in\n\
 which its expression is valid.\n");     
 
@@ -2575,7 +2665,8 @@ which its expression is valid.\n");     
 bpstat
 bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
 {
-  struct breakpoint *b, *temp;
+  struct bp_location *loc, *temp;
+  struct breakpoint *b;
   /* True if we've hit a breakpoint (as opposed to a watchpoint).  */
   int real_breakpoint = 0;
   /* Root of the chain of bpstat's */
@@ -2584,27 +2675,21 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
   bpstat bs = root_bs;
   int thread_id = pid_to_thread_id (ptid);
 
-  ALL_BREAKPOINTS_SAFE (b, temp)
+  ALL_BP_LOCATIONS_SAFE (loc, temp)
   {
+    b = loc->owner;
+
     if (!breakpoint_enabled (b) && b->enable_state != bp_permanent)
       continue;
 
-    if (b->type != bp_watchpoint
-	&& b->type != bp_hardware_watchpoint
-	&& b->type != bp_read_watchpoint
-	&& b->type != bp_access_watchpoint
-	&& b->type != bp_hardware_breakpoint
-	&& 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)	/* a non-watchpoint bp */
+    if (loc->loc_type == bp_loc_software_breakpoint
+	|| loc->loc_type == bp_loc_hardware_breakpoint)
       {
-	if (b->loc->address != bp_addr) 	/* address doesn't match */
+	if (loc->address != bp_addr) 	/* address doesn't match */
 	  continue;
 	if (overlay_debugging		/* unmapped overlay section */
-	    && section_is_overlay (b->loc->section) 
-	    && !section_is_mapped (b->loc->section))
+	    && section_is_overlay (loc->section) 
+	    && !section_is_mapped (loc->section))
 	  continue;
       }
 
@@ -2614,22 +2699,10 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
        in the code (not from a breakpoint) when a hardware watchpoint has 
        been defined.  */
 
-    if ((b->type == bp_hardware_watchpoint
-	 || b->type == bp_read_watchpoint
-	 || b->type == bp_access_watchpoint)
+    if (loc->loc_type == bp_loc_hardware_watchpoint
 	&& !stopped_by_watchpoint)
       continue;
 
-    if (b->type == bp_hardware_breakpoint)
-      {
-	if (b->loc->address != bp_addr)
-	  continue;
-	if (overlay_debugging		/* unmapped overlay section */
-	    && section_is_overlay (b->loc->section) 
-	    && !section_is_mapped (b->loc->section))
-	  continue;
-      }
-
     /* Is this a catchpoint of a load or unload?  If so, did we
        get a load or unload of the specified library?  If not,
        ignore it. */
@@ -2677,7 +2750,7 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
 
     /* Come here if it's a watchpoint, or if the break address matches */
 
-    bs = bpstat_alloc (b, bs);	/* Alloc a bpstat to explain stop */
+    bs = bpstat_alloc (loc, bs);	/* Alloc a bpstat to explain stop */
 
     /* Watchpoints may change this, if not found to have triggered. */
     bs->stop = 1;
@@ -2887,10 +2960,8 @@ bpstat_stop_status (CORE_ADDR bp_addr, p
 
   /* The value of a hardware watchpoint hasn't changed, but the
      intermediate memory locations we are watching may have.  */
-  if (bs && !bs->stop &&
-      (bs->breakpoint_at->type == bp_hardware_watchpoint ||
-       bs->breakpoint_at->type == bp_read_watchpoint ||
-       bs->breakpoint_at->type == bp_access_watchpoint))
+  if (bs && !bs->stop
+      && bs->breakpoint_at->loc_type == bp_loc_hardware_watchpoint)
     {
       remove_breakpoints ();
       insert_breakpoints ();
@@ -3060,7 +3131,7 @@ bpstat_what (bpstat bs)
 	/* I suspect this can happen if it was a momentary breakpoint
 	   which has since been deleted.  */
 	continue;
-      switch (bs->breakpoint_at->type)
+      switch (bs->breakpoint_at->owner->type)
 	{
 	case bp_none:
 	  continue;
@@ -3218,9 +3289,9 @@ bpstat_get_triggered_catchpoints (bpstat
   for (; ep_list != NULL; ep_list = ep_list->next)
     {
       /* Is this eventpoint a catchpoint?  If not, ignore it. */
-      ep = ep_list->breakpoint_at;
-      if (ep == NULL)
+      if (ep_list->breakpoint_at == NULL)
 	break;
+      ep = ep_list->breakpoint_at->owner;
       if ((ep->type != bp_catch_load) &&
 	  (ep->type != bp_catch_unload) &&
 	  (ep->type != bp_catch_catch) &&
@@ -3229,7 +3300,7 @@ bpstat_get_triggered_catchpoints (bpstat
 	continue;
 
       /* Yes; add it to the list. */
-      bs = bpstat_alloc (ep, bs);
+      bs = bpstat_alloc (ep->loc, bs);
       *bs = *ep_list;
       bs->next = NULL;
       bs = root_bs->next;
@@ -4108,6 +4179,25 @@ set_raw_breakpoint (struct symtab_and_li
   return b;
 }
 
+void
+add_location_to_breakpoint (struct breakpoint *b, enum bptype bptype,
+			    const struct symtab_and_line *sal)
+{
+  struct bp_location *loc;
+
+  loc = allocate_bp_location (b, bptype);
+  /* FIXME: Should we add to the end?  */
+  loc->loc_next = b->loc;
+  b->loc = loc;
+  b->loc->requested_address = sal->pc;
+  b->loc->address = adjust_breakpoint_address (b->loc->requested_address,
+                                               bptype);
+  b->loc->section = sal->section;
+
+  check_duplicates (b);
+  breakpoints_changed ();
+}
+
 
 /* Note that the breakpoint object B describes a permanent breakpoint
    instruction, hard-wired into the inferior's code.  */
@@ -4909,6 +4999,9 @@ create_breakpoints (struct symtabs_and_l
 		    int thread, int ignore_count, int from_tty,
 		    struct breakpoint *pending_bp)
 {
+  struct breakpoint *b = NULL;
+  int i = 0;
+
   if (type == bp_hardware_breakpoint)
     {
       int i = hw_breakpoint_used_count ();
@@ -4921,59 +5014,64 @@ create_breakpoints (struct symtabs_and_l
 	error (_("Hardware breakpoints used exceeds limit."));
     }
 
-  /* Now set all the breakpoints.  */
-  {
-    int i;
-    for (i = 0; i < sals.nelts; i++)
-      {
-	struct breakpoint *b;
-	struct symtab_and_line sal = sals.sals[i];
+  /* Set the overall breakpoint.  */
+  /* FIXME: Also creates the first location... all references to SALS or I
+     here are suspect.  */
 
-	if (from_tty)
-	  describe_other_breakpoints (sal.pc, sal.section);
-	
-	b = set_raw_breakpoint (sal, type);
-	set_breakpoint_count (breakpoint_count + 1);
-	b->number = breakpoint_count;
-	b->cond = cond[i];
-	b->thread = thread;
-	if (addr_string[i])
-	  b->addr_string = addr_string[i];
-	else
-	  /* addr_string has to be used or breakpoint_re_set will delete
-	     me.  */
-	  b->addr_string = xstrprintf ("*0x%s", paddr (b->loc->address));
-	b->cond_string = cond_string[i];
-	b->ignore_count = ignore_count;
-	b->enable_state = bp_enabled;
-	b->disposition = disposition;
-	/* If resolving a pending breakpoint, a check must be made to see if
-	   the user has specified a new condition or commands for the 
-	   breakpoint.  A new condition will override any condition that was 
-	   initially specified with the initial breakpoint command.  */
-	if (pending_bp)
-	  {
-	    char *arg;
-	    if (pending_bp->cond_string)
-	      {
-		arg = pending_bp->cond_string;
-		b->cond_string = savestring (arg, strlen (arg));
-		b->cond = parse_exp_1 (&arg, block_for_pc (b->loc->address), 0);
-		if (*arg)
-		  error (_("Junk at end of pending breakpoint condition expression"));
-	      }
-	    /* If there are commands associated with the breakpoint, they should 
-	       be copied too.  */
-	    if (pending_bp->commands)
-	      b->commands = copy_command_lines (pending_bp->commands);
-	    
-	    /* We have to copy over the ignore_count and thread as well.  */
-	    b->ignore_count = pending_bp->ignore_count;
-	    b->thread = pending_bp->thread;
-	  }
-	mention (b);
-      }
-  }    
+  if (from_tty)
+    describe_other_breakpoints (sals.sals[0].pc, sals.sals[0].section);
+
+  b = set_raw_breakpoint (sals.sals[0], type);
+  set_breakpoint_count (breakpoint_count + 1);
+  b->number = breakpoint_count;
+  b->cond = cond[i];
+  b->thread = thread;
+  if (addr_string[i])
+    b->addr_string = addr_string[i];
+  else
+    /* addr_string has to be used or breakpoint_re_set will delete
+       me.  */
+    b->addr_string = xstrprintf ("*0x%s", paddr (b->loc->address));
+  b->cond_string = cond_string[i];
+  b->ignore_count = ignore_count;
+  b->enable_state = bp_enabled;
+  b->disposition = disposition;
+
+  /* If resolving a pending breakpoint, a check must be made to see if
+     the user has specified a new condition or commands for the 
+     breakpoint.  A new condition will override any condition that was 
+     initially specified with the initial breakpoint command.  */
+  if (pending_bp)
+    {
+      char *arg;
+      if (pending_bp->cond_string)
+	{
+	  arg = pending_bp->cond_string;
+	  b->cond_string = savestring (arg, strlen (arg));
+	  b->cond = parse_exp_1 (&arg, block_for_pc (b->loc->address), 0);
+	  if (*arg)
+	    error (_("Junk at end of pending breakpoint condition expression"));
+	}
+      /* If there are commands associated with the breakpoint, they should 
+	 be copied too.  */
+      if (pending_bp->commands)
+	b->commands = copy_command_lines (pending_bp->commands);
+
+      /* We have to copy over the ignore_count and thread as well.  */
+      b->ignore_count = pending_bp->ignore_count;
+      b->thread = pending_bp->thread;
+    }
+
+  /* Now set all the other locations.  */
+  for (i = 1; i < sals.nelts; i++)
+    {
+      if (from_tty)
+	describe_other_breakpoints (sals.sals[i].pc, sals.sals[i].section);
+
+      add_location_to_breakpoint (b, type, &sals.sals[i]);
+    }    
+
+  mention (b);
 }
 
 /* Parse ARG which is assumed to be a SAL specification possibly
@@ -5056,6 +5154,9 @@ breakpoint_sals_to_pc (struct symtabs_an
 		       char *address)
 {    
   int i;
+
+  expand_line_sals (sals);
+
   for (i = 0; i < sals->nelts; i++)
     {
       resolve_sal_pc (&sals->sals[i]);
@@ -5194,7 +5295,7 @@ break_command_1 (char *arg, int flag, in
   if (!pending)
     {
       /* Make sure that all storage allocated to SALS gets freed.  */
-      make_cleanup (xfree, sals.sals);
+      make_cleanup (free_current_contents, &sals.sals);
       
       /* Cleanup the addr_string array but not its contents. */
       make_cleanup (xfree, addr_string);
@@ -5371,7 +5472,7 @@ do_captured_breakpoint (struct ui_out *u
   make_cleanup (xfree, addr_string);
 
   /* Make sure that all storage allocated to SALS gets freed.  */
-  make_cleanup (xfree, sals.sals);
+  make_cleanup (free_current_contents, &sals.sals);
 
   /* Allocate space for all the cond expressions. */
   cond = xcalloc (sals.nelts, sizeof (struct expression *));
@@ -6697,9 +6798,9 @@ breakpoint_auto_delete (bpstat bs)
   struct breakpoint *b, *temp;
 
   for (; bs; bs = bs->next)
-    if (bs->breakpoint_at && bs->breakpoint_at->disposition == disp_del
+    if (bs->breakpoint_at && bs->breakpoint_at->owner->disposition == disp_del
 	&& bs->stop)
-      delete_breakpoint (bs->breakpoint_at);
+      delete_breakpoint (bs->breakpoint_at->owner);
 
   ALL_BREAKPOINTS_SAFE (b, temp)
   {
@@ -6716,7 +6817,7 @@ delete_breakpoint (struct breakpoint *bp
 {
   struct breakpoint *b;
   bpstat bs;
-  struct bp_location *loc;
+  struct bp_location *loc, *loc_temp;
 
   gdb_assert (bpt != NULL);
 
@@ -6740,17 +6841,17 @@ delete_breakpoint (struct breakpoint *bp
     deprecated_delete_breakpoint_hook (bpt);
   breakpoint_delete_event (bpt->number);
 
-  if (bpt->loc->inserted)
-    remove_breakpoint (bpt->loc, mark_inserted);
+  ALL_ONE_BP_LOCATIONS (bpt, loc)
+    {
+      if (loc->inserted)
+	remove_breakpoint (loc, mark_inserted);
 
-  free_valchain (bpt->loc);
+      free_valchain (loc);
+    }
 
   if (breakpoint_chain == bpt)
     breakpoint_chain = bpt->next;
 
-  if (bp_location_chain == bpt->loc)
-    bp_location_chain = bpt->loc->next;
-
   /* If we have callback-style exception catchpoints, don't go through
      the adjustments to the C++ runtime library etc. if the inferior
      isn't actually running.  target_enable_exception_callback for a
@@ -6772,7 +6873,6 @@ delete_breakpoint (struct breakpoint *bp
       do_cleanups (cleanups);
     }
 
-
   ALL_BREAKPOINTS (b)
     if (b->next == bpt)
     {
@@ -6780,80 +6880,86 @@ delete_breakpoint (struct breakpoint *bp
       break;
     }
 
-  ALL_BP_LOCATIONS (loc)
-    if (loc->next == bpt->loc)
-      {
-	loc->next = bpt->loc->next;
-	break;
-      }
-
-  check_duplicates (bpt);
-  /* If this breakpoint was inserted, and there is another breakpoint
-     at the same address, we need to insert the other breakpoint.  */
-  if (bpt->loc->inserted
-      && bpt->type != bp_hardware_watchpoint
-      && bpt->type != bp_read_watchpoint
-      && bpt->type != bp_access_watchpoint
-      && bpt->type != bp_catch_fork
-      && bpt->type != bp_catch_vfork
-      && bpt->type != bp_catch_exec)
-    {
-      ALL_BREAKPOINTS (b)
-	if (b->loc->address == bpt->loc->address
-	    && b->loc->section == bpt->loc->section
-	    && !b->loc->duplicate
-	    && b->enable_state != bp_disabled
-	    && b->enable_state != bp_shlib_disabled
-	    && !b->pending
-	    && b->enable_state != bp_call_disabled)
-	{
-	  int val;
+  /* Delete all breakpoint locations belonging to this breakpoint.
+     Should there be an iterator for this?  */
+  loc = bp_location_chain;
+  while (loc)
+    if (loc->next && loc->next->owner == bpt)
+      loc->next = loc->next->next;
+    else
+      loc = loc->next;
 
-	  /* We should never reach this point if there is a permanent
-	     breakpoint at the same address as the one being deleted.
-	     If there is a permanent breakpoint somewhere, it should
-	     always be the only one inserted.  */
-	  if (b->enable_state == bp_permanent)
-	    internal_error (__FILE__, __LINE__,
-			    _("another breakpoint was inserted on top of "
-			    "a permanent breakpoint"));
+  if (bp_location_chain && bp_location_chain->owner == bpt)
+    bp_location_chain = bp_location_chain->next;
 
-	  if (b->type == bp_hardware_breakpoint)
-	    val = target_insert_hw_breakpoint (b->loc->address, b->loc->shadow_contents);
-	  else
-	    val = target_insert_breakpoint (b->loc->address, b->loc->shadow_contents);
+  check_duplicates (bpt);
 
-	  /* If there was an error in the insert, print a message, then stop execution.  */
-	  if (val != 0)
+  ALL_ONE_BP_LOCATIONS (bpt, loc)
+    /* If this breakpoint was inserted, and there is another breakpoint
+       at the same address, we need to insert the other breakpoint.  */
+    if (loc->inserted
+	&& bpt->type != bp_hardware_watchpoint
+	&& bpt->type != bp_read_watchpoint
+	&& bpt->type != bp_access_watchpoint
+	&& bpt->type != bp_catch_fork
+	&& bpt->type != bp_catch_vfork
+	&& bpt->type != bp_catch_exec)
+      {
+	ALL_BP_LOCATIONS (loc_temp)
+	  if (loc_temp->address == loc->address
+	      && loc_temp->section == loc->section
+	      && !loc_temp->duplicate
+	      && b->enable_state != bp_disabled
+	      && b->enable_state != bp_shlib_disabled
+	      && !b->pending
+	      && b->enable_state != bp_call_disabled)
 	    {
-	      struct ui_file *tmp_error_stream = mem_fileopen ();
-	      make_cleanup_ui_file_delete (tmp_error_stream);
-	     
+	      int val;
+
+	      /* We should never reach this point if there is a permanent
+		 breakpoint at the same address as the one being deleted.
+		 If there is a permanent breakpoint somewhere, it should
+		 always be the only one inserted.  */
+	      if (b->enable_state == bp_permanent)
+		internal_error (__FILE__, __LINE__,
+				_("another breakpoint was inserted on top of "
+				"a permanent breakpoint"));
 
 	      if (b->type == bp_hardware_breakpoint)
+		val = target_insert_hw_breakpoint (loc_temp->address, loc_temp->shadow_contents);
+	      else
+		val = target_insert_breakpoint (loc_temp->address, loc_temp->shadow_contents);
+
+	      /* If there was an error in the insert, print a message, then stop execution.  */
+	      if (val != 0)
 		{
-		  fprintf_unfiltered (tmp_error_stream, 
-					"Cannot insert hardware breakpoint %d.\n"
-				      "You may have requested too many hardware breakpoints.\n",
-					b->number);
-		  }
-		else
-		  {
-		    fprintf_unfiltered (tmp_error_stream, "Cannot insert breakpoint %d.\n", b->number);
-		    fprintf_filtered (tmp_error_stream, "Error accessing memory address ");
-		    deprecated_print_address_numeric (b->loc->address, 1, tmp_error_stream);
-		    fprintf_filtered (tmp_error_stream, ": %s.\n",
-				      safe_strerror (val));
-		  }
-	      
-	      fprintf_unfiltered (tmp_error_stream,"The same program may be running in another process.");
-	      target_terminal_ours_for_output ();
-	      error_stream(tmp_error_stream); 
+		  struct ui_file *tmp_error_stream = mem_fileopen ();
+		  make_cleanup_ui_file_delete (tmp_error_stream);
+
+		  if (b->type == bp_hardware_breakpoint)
+		    {
+		      fprintf_unfiltered (tmp_error_stream, 
+					  "Cannot insert hardware breakpoint %d.\n"
+					  "You may have requested too many hardware breakpoints.\n",
+					  b->number);
+		    }
+		  else
+		    {
+		      fprintf_unfiltered (tmp_error_stream, "Cannot insert breakpoint %d.\n", b->number);
+		      fprintf_filtered (tmp_error_stream, "Error accessing memory address ");
+		      deprecated_print_address_numeric (loc_temp->address, 1, tmp_error_stream);
+		      fprintf_filtered (tmp_error_stream, ": %s.\n",
+					safe_strerror (val));
+		    }
+
+		  fprintf_unfiltered (tmp_error_stream,"The same program may be running in another process.");
+		  target_terminal_ours_for_output ();
+		  error_stream(tmp_error_stream); 
+		}
+	      else
+		loc_temp->inserted = 1;
 	    }
-	  else
-	    b->loc->inserted = 1;
-	}
-    }
+      }
 
   free_command_lines (&bpt->commands);
   if (bpt->cond)
@@ -6881,7 +6987,7 @@ delete_breakpoint (struct breakpoint *bp
   /* FIXME, how can we find all bpstat's?
      We just check stop_bpstat for now.  */
   for (bs = stop_bpstat; bs; bs = bs->next)
-    if (bs->breakpoint_at == bpt)
+    if (bs->breakpoint_at && bs->breakpoint_at->owner == bpt)
       {
 	bs->breakpoint_at = NULL;
 	bs->old_val = NULL;
@@ -6891,7 +6997,9 @@ delete_breakpoint (struct breakpoint *bp
      bp, we mark it as deleted before freeing its storage. */
   bpt->type = bp_none;
 
-  xfree (bpt->loc);
+  ALL_ONE_BP_LOCATIONS_SAFE (bpt, loc, loc_temp)
+    xfree (loc);
+
   xfree (bpt);
 }
 
@@ -7625,6 +7733,17 @@ decode_line_spec_1 (char *string, int fu
   return sals;
 }
 
+/* Return true if breakpoint B is associated with the code address ADDR.  */
+
+int
+breakpoint_at_address_p (struct breakpoint *b,
+			 CORE_ADDR addr)
+{
+  return ((b->loc->loc_type == bp_loc_software_breakpoint
+	   || b->loc->loc_type == bp_loc_hardware_breakpoint)
+	  && b->loc->address == addr);
+}
+
 void
 _initialize_breakpoint (void)
 {
Index: src/gdb/linespec.c
===================================================================
--- src.orig/gdb/linespec.c	2005-03-13 14:38:40.000000000 -0500
+++ src/gdb/linespec.c	2005-03-13 17:45:48.000000000 -0500
@@ -91,7 +91,7 @@ static int add_matching_methods (int met
 static int add_constructors (int method_counter, struct type *t,
 			     struct symbol **sym_arr);
 
-static void build_canonical_line_spec (struct symtab_and_line *,
+static void build_canonical_line_spec (struct symtabs_and_lines *,
 				       char *, char ***);
 
 static char *find_toplevel_char (char *s, char c);
@@ -381,22 +381,17 @@ add_constructors (int method_counter, st
    line spec is `filename:linenum'.  */
 
 static void
-build_canonical_line_spec (struct symtab_and_line *sal, char *symname,
-			   char ***canonical)
+build_one_canonical_line_spec (struct symtab_and_line *sal, char *symname,
+			       char **canonical)
 {
-  char **canonical_arr;
   char *canonical_name;
   char *filename;
   struct symtab *s = sal->symtab;
 
   if (s == (struct symtab *) NULL
-      || s->filename == (char *) NULL
-      || canonical == (char ***) NULL)
+      || s->filename == (char *) NULL)
     return;
 
-  canonical_arr = (char **) xmalloc (sizeof (char *));
-  *canonical = canonical_arr;
-
   filename = s->filename;
   if (symname != NULL)
     {
@@ -408,7 +403,25 @@ build_canonical_line_spec (struct symtab
       canonical_name = xmalloc (strlen (filename) + 30);
       sprintf (canonical_name, "%s:%d", filename, sal->line);
     }
-  canonical_arr[0] = canonical_name;
+  *canonical = canonical_name;
+}
+
+static void
+build_canonical_line_spec (struct symtabs_and_lines *sals, char *symname,
+			   char ***canonical)
+{
+  char **canonical_arr;
+  struct symtab_and_line *sal;
+  int i;
+
+  if (canonical == (char ***) NULL)
+    return;
+
+  canonical_arr = (char **) xmalloc (sizeof (char *) * sals->nelts);
+  *canonical = canonical_arr;
+
+  for (i = 0; i < sals->nelts; i++)
+    build_one_canonical_line_spec (&sals->sals[i], symname, &canonical_arr[i]);
 }
 
 
@@ -1131,7 +1144,7 @@ decode_objc (char **argptr, int funfirst
 	{
 	  /* Canonicalize this, so it remains resolved for dylib loads.  */
 	  values.sals[0] = find_function_start_sal (sym, funfirstline);
-	  build_canonical_line_spec (values.sals, SYMBOL_NATURAL_NAME (sym), canonical);
+	  build_canonical_line_spec (&values, SYMBOL_NATURAL_NAME (sym), canonical);
 	}
       else
 	{
@@ -1542,7 +1555,6 @@ symtab_from_filename (char **argptr, cha
 }
 
 
-
 /* This decodes a line where the argument is all digits (possibly
    preceded by a sign).  Q should point to the end of those digits;
    the other arguments are as usual.  */
@@ -1623,11 +1635,47 @@ decode_all_digits (char **argptr, struct
     xmalloc (sizeof (struct symtab_and_line));
   values.sals[0] = val;
   values.nelts = 1;
+
   if (need_canonical)
-    build_canonical_line_spec (values.sals, NULL, canonical);
+    build_canonical_line_spec (&values, NULL, canonical);
+
   return values;
 }
 
+void
+expand_line_sals (struct symtabs_and_lines *values)
+{
+  struct symtabs_and_lines ret, this_line;
+  int i, j;
+
+  ret.nelts = 0;
+  ret.sals = NULL;
+  for (i = 0; i < values->nelts; i++)
+    {
+      if (values->sals[i].symtab == NULL
+	  || values->sals[i].line == 0
+	  || values->sals[i].pc != 0)
+	{
+	  ret.sals = xrealloc (ret.sals, sizeof (ret.sals[0]) * (ret.nelts + 1));
+	  ret.sals[ret.nelts++] = values->sals[i];
+	  continue;
+	}
+
+      /* FIXME: This doesn't work right without -readnow.  */
+      /* FIXME: This doesn't handle optimized code correctly; we should not always
+	 return all fragments of a line.  */
+      extern struct symtabs_and_lines find_all_lines (struct symtab *, int);
+      this_line = find_all_lines (values->sals[i].symtab, values->sals[i].line);
+
+      ret.sals = xrealloc (ret.sals, sizeof (ret.sals[0]) * (ret.nelts + this_line.nelts));
+      for (j = 0; j < this_line.nelts; j++)
+	ret.sals[ret.nelts++] = this_line.sals[j];
+      xfree (this_line.sals);
+    }
+  xfree (values->sals);
+  *values = ret;
+}
+
 
 
 /* Decode a linespec starting with a dollar sign.  */
@@ -1697,7 +1745,7 @@ decode_dollar (char *copy, int funfirstl
   values.nelts = 1;
 
   if (need_canonical)
-    build_canonical_line_spec (values.sals, NULL, canonical);
+    build_canonical_line_spec (&values, NULL, canonical);
 
   return values;
 }
@@ -1779,7 +1827,7 @@ symbol_found (int funfirstline, char ***
 	  struct blockvector *bv = BLOCKVECTOR (sym_symtab);
 	  struct block *b = BLOCKVECTOR_BLOCK (bv, STATIC_BLOCK);
 	  if (lookup_block_symbol (b, copy, NULL, VAR_DOMAIN) != NULL)
-	    build_canonical_line_spec (values.sals, copy, canonical);
+	    build_canonical_line_spec (&values, copy, canonical);
 	}
       return values;
     }
Index: src/gdb/breakpoint.h
===================================================================
--- src.orig/gdb/breakpoint.h	2005-03-13 14:38:09.000000000 -0500
+++ src/gdb/breakpoint.h	2005-03-13 14:48:09.000000000 -0500
@@ -185,78 +185,7 @@ enum target_hw_bp_type
     hw_execute = 3		/* Execute HW breakpoint */
   };
 
-/* GDB maintains two types of information about each breakpoint (or
-   watchpoint, or other related event).  The first type corresponds
-   to struct breakpoint; this is a relatively high-level structure
-   which contains the source location(s), stopping conditions, user
-   commands to execute when the breakpoint is hit, and so forth.
-
-   The second type of information corresponds to struct bp_location.
-   Each breakpoint has one or (eventually) more locations associated
-   with it, which represent target-specific and machine-specific
-   mechanisms for stopping the program.  For instance, a watchpoint
-   expression may require multiple hardware watchpoints in order to
-   catch all changes in the value of the expression being watched.  */
-
-enum bp_loc_type
-{
-  bp_loc_software_breakpoint,
-  bp_loc_hardware_breakpoint,
-  bp_loc_hardware_watchpoint,
-  bp_loc_other			/* Miscellaneous...  */
-};
-
-struct bp_location
-{
-  /* Chain pointer to the next breakpoint location.  */
-  struct bp_location *next;
-
-  /* Type of this breakpoint location.  */
-  enum bp_loc_type loc_type;
-
-  /* Each breakpoint location must belong to exactly one higher-level
-     breakpoint.  This and the DUPLICATE flag are more straightforward
-     than reference counting.  */
-  struct breakpoint *owner;
-
-  /* Nonzero if this breakpoint is now inserted.  */
-  char inserted;
-
-  /* Nonzero if this is not the first breakpoint in the list
-     for the given address.  */
-  char duplicate;
-
-  /* If we someday support real thread-specific breakpoints, then
-     the breakpoint location will need a thread identifier.  */
-
-  /* Data for specific breakpoint types.  These could be a union, but
-     simplicity is more important than memory usage for breakpoints.  */
-
-  /* Note that zero is a perfectly valid code address on some platforms
-     (for example, the mn10200 (OBSOLETE) and mn10300 simulators).  NULL
-     is not a special value for this field.  Valid for all types except
-     bp_loc_other.  */
-  CORE_ADDR address;
-
-  /* For any breakpoint type with an address, this is the BFD section
-     associated with the address.  Used primarily for overlay debugging.  */
-  asection *section;
-
-  /* "Real" contents of byte where breakpoint has been inserted.
-     Valid only when breakpoints are in the program.  Under the complete
-     control of the target insert_breakpoint and remove_breakpoint routines.
-     No other code should assume anything about the value(s) here.
-     Valid only for bp_loc_software_breakpoint.  */
-  char shadow_contents[BREAKPOINT_MAX];
-
-  /* Address at which breakpoint was requested, either by the user or
-     by GDB for internal breakpoints.  This will usually be the same
-     as ``address'' (above) except for cases in which
-     ADJUST_BREAKPOINT_ADDRESS has computed a different address at
-     which to place the breakpoint in order to comply with a
-     processor's architectual constraints.  */
-  CORE_ADDR requested_address;
-};
+struct bp_location;
 
 /* This structure is a collection of function pointers that, if available,
    will be called instead of the performing the default action for this
@@ -300,10 +229,12 @@ struct breakpoint
     struct bp_location *loc;
 
     /* Line number of this address.  */
+    /* FIXME: This should be part of the bp_location.  */
 
     int line_number;
 
     /* Source file name of this address.  */
+    /* FIXME: This should be part of the bp_location.  */
 
     char *source_file;
 
@@ -577,8 +508,8 @@ struct bpstats
     /* Linked list because there can be two breakpoints at the same
        place, and a bpstat reflects the fact that both have been hit.  */
     bpstat next;
-    /* Breakpoint that we are at.  */
-    struct breakpoint *breakpoint_at;
+    /* Breakpoint location that we are at.  */
+    struct bp_location *breakpoint_at;
     /* Commands left to be done.  */
     struct command_line *commands;
     /* Old value associated with a watchpoint.  */
@@ -805,4 +736,7 @@ extern int deprecated_exception_catchpoi
    reinitialized -- e.g. when program is re-run.  */
 extern int deprecated_exception_support_initialized;
 
+/* Return true if the breakpoint is associated with the code address.  */
+extern int breakpoint_at_address_p (struct breakpoint *, CORE_ADDR);
+
 #endif /* !defined (BREAKPOINT_H) */
Index: src/gdb/symtab.c
===================================================================
--- src.orig/gdb/symtab.c	2005-03-13 14:38:41.000000000 -0500
+++ src/gdb/symtab.c	2005-03-13 17:06:03.000000000 -0500
@@ -73,7 +73,7 @@ static void sources_info (char *, int);
 
 static void output_source_filename (const char *, int *);
 
-static int find_line_common (struct linetable *, int, int *);
+static int find_line_common (struct linetable *, int, int *, int, int **);
 
 /* This one is used by linespec.c */
 
@@ -2217,6 +2217,86 @@ find_pc_line (CORE_ADDR pc, int notcurre
   return find_pc_sect_line (pc, section, notcurrent);
 }
 
+struct symtabs_and_lines
+find_all_lines (struct symtab *initial_symtab, int line)
+{
+  int exact = 0;
+  const char *filename = initial_symtab->filename;
+
+  /* BEST_INDEX and BEST_LINETABLE identify the smallest linenumber > LINE
+     so far seen.  */
+
+  int best_line = 0;
+  int best_index = 0;
+  struct linetable *best_linetable = NULL;
+  struct symtab *best_symtab = NULL;
+  struct objfile *objfile;
+  struct symtab *s;
+  struct symtabs_and_lines sals;
+
+  sals.nelts = 0;
+  sals.sals = NULL;
+
+  ALL_SYMTABS (objfile, s)
+    {
+      int new_exact;
+      int *new_matches;
+      int ind;
+      struct linetable *l;
+
+      if (strcmp (filename, s->filename) != 0)
+	continue;
+
+      l = LINETABLE (s);
+      ind = find_line_common (l, line, &new_exact, 1, &new_matches);
+      if (new_exact)
+	{
+	  exact = 1;
+	  sals.sals = xrealloc (sals.sals, (sals.nelts + ind) * sizeof (sals.sals[0]));
+	  while (ind > 0)
+	    {
+	      sals.sals[sals.nelts].symtab = s;
+	      sals.sals[sals.nelts].section = NULL;
+	      sals.sals[sals.nelts].end = 0;
+	      sals.sals[sals.nelts].line = line;
+	      sals.sals[sals.nelts++].pc = l->item[new_matches[--ind]].pc;
+	    }
+	}
+      else if (!exact && ind >= 0
+	       && (best_line == 0 || l->item[ind].line < best_line))
+	{
+	  best_line = l->item[ind].line;
+	  best_index = ind;
+	  best_linetable = l;
+	  best_symtab = s;
+	}
+    }
+
+  if (sals.nelts == 0)
+    {
+      sals.nelts = 1;
+      sals.sals = xmalloc (sizeof (sals.sals[0]));
+      if (best_line != 0)
+	{
+	  sals.sals[0].symtab = best_symtab;
+	  sals.sals[0].line = best_linetable->item[best_index].line;
+	  sals.sals[0].section = NULL;
+	  sals.sals[0].end = 0;
+	  sals.sals[0].pc = best_linetable->item[best_index].pc;
+	}
+      else
+	{
+	  sals.sals[0].symtab = initial_symtab;
+	  sals.sals[0].section = NULL;
+	  sals.sals[0].end = 0;
+	  sals.sals[0].line = line;
+	  sals.sals[0].pc = 0;
+	}
+    }
+  
+  return sals;
+}
+
 /* Find line number LINE in any symtab whose name is the same as
    SYMTAB.
 
@@ -2242,7 +2322,7 @@ find_line_symtab (struct symtab *symtab,
   /* First try looking it up in the given symtab.  */
   best_linetable = LINETABLE (symtab);
   best_symtab = symtab;
-  best_index = find_line_common (best_linetable, line, &exact);
+  best_index = find_line_common (best_linetable, line, &exact, 0, NULL);
   if (best_index < 0 || !exact)
     {
       /* Didn't find an exact match.  So we better keep looking for
@@ -2273,7 +2353,7 @@ find_line_symtab (struct symtab *symtab,
 	if (strcmp (symtab->filename, s->filename) != 0)
 	  continue;
 	l = LINETABLE (s);
-	ind = find_line_common (l, line, &exact);
+	ind = find_line_common (l, line, &exact, 0, NULL);
 	if (ind >= 0)
 	  {
 	    if (exact)
@@ -2377,7 +2457,7 @@ find_line_pc_range (struct symtab_and_li
 
 static int
 find_line_common (struct linetable *l, int lineno,
-		  int *exact_match)
+		  int *exact_match, int want_all, int **all_lines)
 {
   int i;
   int len;
@@ -2389,6 +2469,10 @@ find_line_common (struct linetable *l, i
   int best_index = -1;
   int best = 0;
 
+  int allocated = 0;
+  int n_matches = 0;
+  int *matches = NULL;
+
   if (lineno <= 0)
     return -1;
   if (l == 0)
@@ -2403,7 +2487,21 @@ find_line_common (struct linetable *l, i
 	{
 	  /* Return the first (lowest address) entry which matches.  */
 	  *exact_match = 1;
-	  return i;
+	  if (!want_all)
+	    return i;
+
+	  if (allocated == 0)
+	    {
+	      allocated = 2;
+	      matches = xmalloc (allocated * sizeof (int));
+	    }
+	  else if (allocated == n_matches)
+	    {
+	      allocated *= 2;
+	      matches = xrealloc (matches, allocated * sizeof (int));
+	    }
+	  matches[n_matches++] = i;
+	  best = lineno;
 	}
 
       if (item->line > lineno && (best == 0 || item->line < best))
@@ -2413,6 +2511,12 @@ find_line_common (struct linetable *l, i
 	}
     }
 
+  if (want_all && matches != NULL)
+    {
+      *all_lines = matches;
+      return n_matches;
+    }
+
   /* If we got here, we didn't get an exact match.  */
 
   *exact_match = 0;
Index: src/gdb/testsuite/gdb.base/multi-break.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.base/multi-break.c	2005-03-13 18:55:09.000000000 -0500
@@ -0,0 +1,32 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ 
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+int
+bar (void)
+{
+  int b = 4;
+#include "multi-break.h"
+}
+
+int
+main (int argc, char **argv)
+{
+  int b = bar ();
+#include "multi-break.h"
+}
Index: src/gdb/testsuite/gdb.base/multi-break.exp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.base/multi-break.exp	2005-03-13 19:20:25.000000000 -0500
@@ -0,0 +1,50 @@
+# Copyright 2005 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  
+
+# C testcases for breakpoints which are set in multiple locations.
+
+set testfile "multi-break"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+     return -1
+}
+
+gdb_exit
+gdb_start
+gdb_load ${binfile}
+
+if ![runto_main] then {
+    perror "couldn't run to breakpoint"
+    continue
+} 
+
+# Set a breakpoint in a header which is included twice.
+# Note: the text of the warning is inappropriate...
+set header_line [gdb_get_line_number "set first breakpoint here" "${testfile}.h"]
+gdb_test "break ${testfile}.h:$header_line" \
+  "Breakpoint $decimal at $hex: file multi-break.h, line $header_line\\.\r\nwarning: Multiple breakpoints.*"
+
+# Make sure we hit the breakpoint twice.
+
+gdb_test "continue" \
+  "Breakpoint $decimal, bar \\(\\) at ${testfile}.h:$header_line\r\n.*return.*" \
+  "continue to header breakpoint in bar"
+
+gdb_test "continue" \
+  "Breakpoint $decimal, main \\(.*\\) at ${testfile}.h:$header_line\r\n.*return.*" \
+  "continue to header breakpoint in main"
Index: src/gdb/testsuite/gdb.base/multi-break.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ src/gdb/testsuite/gdb.base/multi-break.h	2005-03-13 19:00:11.000000000 -0500
@@ -0,0 +1,21 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2005 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+ 
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+   USA.  */
+
+  int a = 2;
+  return a * b; /* set first breakpoint here */


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