[PATCH] Fix breakpoint updates for multi-inferior

Luis Gustavo
2012-01-25T20:08:20Z

[-- Attachment #1: Type: text/plain, Size: 1377 bytes --]

Hi,

While working on the target-side condition evaluation patch, i stumbled
upon the strange situation where GDB had more than a single "inserted"
location in a list with multiple duplicate locations.

Further investigation showed that the logic for finding the first
inserted location at a specific address does not work when multiple
inferiors are being debugged. This code is inside
update_global_location_list (...).

This is partly because we expect the list of locations to be sorted by
breakpoint numbers and addresses.

Suppose we're going through locations at address FOO, and we already
defined the "first" for that set of locations. When a location does not
match the "first" location of that address, we then assume we've gone
past the locations at address FOO. This is correct for single inferiors.

Now, consider a multi-inferior scenario and breakpoints with locations
on multiple inferiors. The code will fail to match two locations due to
the difference between the locations' program spaces, thus failing to
mark duplicate locations correctly.

This patch solves this by updating the locations one program space at a
time, thus preventing multiple insertions of the same location.

This bug shows up when doing multi-inferior debugging in GDBServer. You
will notice GDB sending multiple insert/remove requests for the same
address.

OK?

Luis

[-- Attachment #2: 0001-fix_multiprocess_breaks.diff --]
[-- Type: text/x-patch, Size: 6984 bytes --]

2012-01-25 Luis Machado <lgustavo@codesourcery.com>

* breakpoint.c (update_global_location_list): Handle *point
updates one program space at a time.
* progspace.h (ALL_PROGRAM_SPACES): New helper macro.

Index: gdb/gdb/breakpoint.c
===================================================================
--- gdb.orig/gdb/breakpoint.c 2012-01-25 10:19:35.453821938 -0200
+++ gdb/gdb/breakpoint.c 2012-01-25 11:11:17.721821938 -0200
@@ -10379,6 +10379,7 @@ update_global_location_list (int should_
built bp_location from the current state of ALL_BREAKPOINTS. */
struct bp_location **old_location, **old_locp;
unsigned old_location_count;
+ struct program_space *cur_pspace;

old_location = bp_location;
old_location_count = bp_location_count;
@@ -10586,72 +10587,88 @@ update_global_location_list (int should_
}
}

- /* Rescan breakpoints at the same address and section, marking the
- first one as "first" and any others as "duplicates". This is so
- that the bpt instruction is only inserted once. If we have a
- permanent breakpoint at the same place as BPT, make that one the
- official one, and the rest as duplicates. Permanent breakpoints
- are sorted first for the same address.
-
- Do the same for hardware watchpoints, but also considering the
- watchpoint's type (regular/access/read) and length. */
-
- bp_loc_first = NULL;
- wp_loc_first = NULL;
- awp_loc_first = NULL;
- rwp_loc_first = NULL;
- ALL_BP_LOCATIONS (loc, locp)
+ /* For each program space, go through the list of *points for that specific
+ program space. This is needed due to assumption (by the code below) that
+ two locations don't match only if they don't have the same address/type,
+ since the location list is sorted first by addresses/breakpoint numbers.
+ The locations also don't match when their program spaces are different
+ (multiprocess). */
+
+ ALL_PROGRAM_SPACES (cur_pspace)
{
- /* ALL_BP_LOCATIONS bp_location has LOC->OWNER always
- non-NULL. */
- struct bp_location **loc_first_p;
- b = loc->owner;
-
- if (!should_be_inserted (loc)
- || !breakpoint_address_is_meaningful (b)
- /* Don't detect duplicate for tracepoint locations because they are
- never duplicated. See the comments in field `duplicate' of
- `struct bp_location'. */
- || is_tracepoint (b))
- continue;
-
- /* Permanent breakpoint should always be inserted. */
- if (b->enable_state == bp_permanent && ! loc->inserted)
- internal_error (__FILE__, __LINE__,
- _("allegedly permanent breakpoint is not "
- "actually inserted"));
-
- if (b->type == bp_hardware_watchpoint)
- loc_first_p = &wp_loc_first;
- else if (b->type == bp_read_watchpoint)
- loc_first_p = &rwp_loc_first;
- else if (b->type == bp_access_watchpoint)
- loc_first_p = &awp_loc_first;
- else
- loc_first_p = &bp_loc_first;
-
- if (*loc_first_p == NULL
- || (overlay_debugging && loc->section != (*loc_first_p)->section)
- || !breakpoint_locations_match (loc, *loc_first_p))
+ /* Rescan breakpoints at the same address and section, marking the
+ first one as "first" and any others as "duplicates". This is so
+ that the bpt instruction is only inserted once. If we have a
+ permanent breakpoint at the same place as BPT, make that one the
+ official one, and the rest as duplicates. Permanent breakpoints
+ are sorted first for the same address.
+
+ Do the same for hardware watchpoints, but also considering the
+ watchpoint's type (regular/access/read) and length. */
+
+ bp_loc_first = NULL;
+ wp_loc_first = NULL;
+ awp_loc_first = NULL;
+ rwp_loc_first = NULL;
+
+ ALL_BP_LOCATIONS (loc, locp)
{
- *loc_first_p = loc;
- loc->duplicate = 0;
- continue;
- }
+ /* ALL_BP_LOCATIONS bp_location has LOC->OWNER always
+ non-NULL. */
+ struct bp_location **loc_first_p;
+ b = loc->owner;
+
+ /* Skip this location if it does not belong to the current
+ program space we are looking for. */
+ if (loc->pspace != cur_pspace)
+ continue;
+
+ if (!should_be_inserted (loc)
+ || !breakpoint_address_is_meaningful (b)
+ /* Don't detect duplicate for tracepoint locations because they
+ are never duplicated. See the comments in field `duplicate' of
+ ` struct bp_location'. */
+ || is_tracepoint (b))
+ continue;
+
+ /* Permanent breakpoint should always be inserted. */
+ if (b->enable_state == bp_permanent && ! loc->inserted)
+ internal_error (__FILE__, __LINE__,
+ _("allegedly permanent breakpoint is not "
+ "actually inserted"));
+
+ if (b->type == bp_hardware_watchpoint)
+ loc_first_p = &wp_loc_first;
+ else if (b->type == bp_read_watchpoint)
+ loc_first_p = &rwp_loc_first;
+ else if (b->type == bp_access_watchpoint)
+ loc_first_p = &awp_loc_first;
+ else
+ loc_first_p = &bp_loc_first;

+ if (*loc_first_p == NULL
+ || (overlay_debugging && loc->section != (*loc_first_p)->section)
+ || !breakpoint_locations_match (loc, *loc_first_p))
+ {
+ *loc_first_p = loc;
+ loc->duplicate = 0;
+ continue;
+ }

- /* This and the above ensure the invariant that the first location
- is not duplicated, and is the inserted one.
- All following are marked as duplicated, and are not inserted. */
- if (loc->inserted)
- swap_insertion (loc, *loc_first_p);
- loc->duplicate = 1;
-
- if ((*loc_first_p)->owner->enable_state == bp_permanent && loc->inserted
- && b->enable_state != bp_permanent)
- internal_error (__FILE__, __LINE__,
- _("another breakpoint was inserted on top of "
- "a permanent breakpoint"));
+ /* This and the above ensure the invariant that the first location
+ is not duplicated, and is the inserted one.
+ All following are marked as duplicated, and are not inserted. */
+ if (loc->inserted)
+ swap_insertion (loc, *loc_first_p);
+ loc->duplicate = 1;
+
+ if ((*loc_first_p)->owner->enable_state == bp_permanent
+ && loc->inserted
+ && b->enable_state != bp_permanent)
+ internal_error (__FILE__, __LINE__,
+ _("another breakpoint was inserted on top of "
+ "a permanent breakpoint"));
+ }
}

if (breakpoints_always_inserted_mode () && should_insert
Index: gdb/gdb/progspace.h
===================================================================
--- gdb.orig/gdb/progspace.h 2012-01-25 10:21:24.321821938 -0200
+++ gdb/gdb/progspace.h 2012-01-25 10:41:55.701821938 -0200
@@ -193,6 +193,13 @@ struct program_space
unsigned num_data;
};

+/* Iterate over all program spaces. */
+
+#define ALL_PROGRAM_SPACES(PSPACE) \
+ for (PSPACE = program_spaces; \
+ PSPACE; \
+ PSPACE = PSPACE->next)
+
/* The object file that the main symbol table was loaded from (e.g. the
argument to the "symbol-file" or "file" command). */