Re: eliminate deprecated_insert_raw_breakpoint. what's left.

Pedro Alves
2014-09-09T17:33:02Z

On 09/08/2014 06:46 PM, Pedro Alves wrote:
> Hey,
>
> I'm working on a patch that converts software single-step breakpoints
> to real breakpoints. It's largely done, but needs a bit of cleaning up
> before I'll post it. One issue is that software single-step breakpoints
> were implemented on top of deprecated_insert_raw_breakpoint. They no
> longer are after my patch, but we're still left with some ugly code in
> breakpoint.c related to deprecated_insert_raw_breakpoints on top of other
> breakpoints that I'd like to eliminate completely, but that can only
> be done once all users of deprecated_insert_raw_breakpoint are either converted
> to some more modern mechanism, or eliminated.

Here's my current WIP patch. This runs regression free on x86_64 Fedora 20,
and also on top of my software-single-step-on-x86 series. I've tested this on
ppc64 F18 as well, to cover the step-over-watchpoints changes.

The ultimate goal was to eliminate a bunch of globals and make it possible
to have multiple threads in parallel doing software single-steps (it works!).
Currently in non-stop, on software single-step targets, we force all
single-steps as displaced-stepping operations, which means single-step requests
are serialized, because there's only one scratch pad.

Note this also removes the limitation that you can only have at most two
software single-step breakpoints, because each thread's single sss
breakpoint can hold an arbitrary number of locations.

As mentioned before, this removes deprecated_insert_raw_breakpoint
completely, which we can't so while AIX and IRIX aren't cleaned up.
Or maybe we can. :-)

If something horrible jumps back at you, please shout! :-)

---
From c62b21283aef99bd495aea9b6c6680a4219ca2b2 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Tue, 9 Sep 2014 17:28:45 +0100
Subject: [PATCH] make single-step breakpoints real breakpoints

---
gdb/breakpoint.c | 418 ++++++++++++++----------------------------------------
gdb/breakpoint.h | 33 ++---
gdb/gdbthread.h | 25 ++++
gdb/infrun.c | 360 +++++++++++++++++++++++-----------------------
gdb/infrun.h | 4 +
gdb/record-full.c | 8 +-
gdb/thread.c | 73 +++++++---
7 files changed, 387 insertions(+), 534 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 683ed2b..93f4ad2 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -226,11 +226,6 @@ static void stopat_command (char *arg, int from_tty);

static void tcatch_command (char *arg, int from_tty);

-static void detach_single_step_breakpoints (void);
-
-static int find_single_step_breakpoint (struct address_space *aspace,
- CORE_ADDR pc);
-
static void free_bp_location (struct bp_location *loc);
static void incref_bp_location (struct bp_location *loc);
static void decref_bp_location (struct bp_location **loc);
@@ -296,12 +291,6 @@ static struct breakpoint_ops bkpt_probe_breakpoint_ops;
/* Dynamic printf class type. */
struct breakpoint_ops dprintf_breakpoint_ops;

-/* One (or perhaps two) breakpoints used for software single
- stepping. */
-
-static void *single_step_breakpoints[2];
-static struct gdbarch *single_step_gdbarch[2];
-
/* The style in which to perform a dynamic printf. This is a user
option because different output options have different tradeoffs;
if GDB does the printing, there is better error handling if there
@@ -1616,21 +1605,6 @@ breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org,
memaddr, len, &bl->target_info, bl->gdbarch);
}
-
- /* Now process single-step breakpoints. These are not found in the
- bp_location array. */
- for (i = 0; i < 2; i++)
- {
- struct bp_target_info *bp_tgt = single_step_breakpoints[i];
-
- if (bp_tgt != NULL)
- {
- struct gdbarch *gdbarch = single_step_gdbarch[i];
-
- one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org,
- memaddr, len, bp_tgt, gdbarch);
- }
- }
}

\f
@@ -2089,7 +2063,32 @@ should_be_inserted (struct bp_location *bl)
|| bl->loc_type == bp_loc_hardware_breakpoint)
&& stepping_past_instruction_at (bl->pspace->aspace,
bl->address))
- return 0;
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: skipping breakpoint: "
+ "stepping past insn at: %s\n",
+ paddress (bl->gdbarch, bl->address));
+ }
+ return 0;
+ }
+
+ /* Don't insert watchpoints if we're trying to step past the
+ instruction that triggered one. */
+ if ((bl->loc_type == bp_loc_hardware_watchpoint)
+ && stepping_past_nonsteppable_watchpoint ())
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping past non-steppable watchpoint. "
+ "skipping watchpoint at %s:%d\n",
+ paddress (bl->gdbarch, bl->address),
+ bl->length);
+ }
+ return 0;
+ }

return 1;
}
@@ -3753,9 +3752,6 @@ detach_breakpoints (ptid_t ptid)
val |= remove_breakpoint_1 (bl, mark_inserted);
}

- /* Detach single-step breakpoints as well. */
- detach_single_step_breakpoints ();
-
do_cleanups (old_chain);
return val;
}
@@ -4025,6 +4021,10 @@ breakpoint_init_inferior (enum inf_context context)

/* Also remove step-resume breakpoints. */

+ case bp_single_step:
+
+ /* Also remove single-step breakpoints. */
+
delete_breakpoint (b);
break;

@@ -4121,14 +4121,10 @@ moribund_breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc)
return 0;
}

-/* Returns non-zero if there's a breakpoint inserted at PC, which is
- inserted using regular breakpoint_chain / bp_location array
- mechanism. This does not check for single-step breakpoints, which
- are inserted and removed using direct target manipulation. */
+/* Returns non-zero iff there's a breakpoint inserted at PC. */

int
-regular_breakpoint_inserted_here_p (struct address_space *aspace,
- CORE_ADDR pc)
+breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc)
{
struct bp_location *bl, **blp_tmp;

@@ -4152,27 +4148,12 @@ regular_breakpoint_inserted_here_p (struct address_space *aspace,
return 0;
}

-/* Returns non-zero iff there's either regular breakpoint
- or a single step breakpoint inserted at PC. */
+/* This function returns non-zero iff there is a software breakpoint
+ inserted at PC. */

int
-breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc)
-{
- if (regular_breakpoint_inserted_here_p (aspace, pc))
- return 1;
-
- if (single_step_breakpoint_inserted_here_p (aspace, pc))
- return 1;
-
- return 0;
-}
-
-/* Ignoring deprecated raw breakpoints, return non-zero iff there is a
- software breakpoint inserted at PC. */
-
-static struct bp_location *
-find_non_raw_software_breakpoint_inserted_here (struct address_space *aspace,
- CORE_ADDR pc)
+software_breakpoint_inserted_here_p (struct address_space *aspace,
+ CORE_ADDR pc)
{
struct bp_location *bl, **blp_tmp;

@@ -4190,27 +4171,10 @@ find_non_raw_software_breakpoint_inserted_here (struct address_space *aspace,
&& !section_is_mapped (bl->section))
continue; /* unmapped overlay -- can't be a match */
else
- return bl;
+ return 1;
}
}

- return NULL;
-}
-
-/* This function returns non-zero iff there is a software breakpoint
- inserted at PC. */
-
-int
-software_breakpoint_inserted_here_p (struct address_space *aspace,
- CORE_ADDR pc)
-{
- if (find_non_raw_software_breakpoint_inserted_here (aspace, pc) != NULL)
- return 1;
-
- /* Also check for software single-step breakpoints. */
- if (single_step_breakpoint_inserted_here_p (aspace, pc))
- return 1;
-
return 0;
}

@@ -5498,6 +5462,7 @@ bpstat_stop_status (struct address_space *aspace,
}
}

+ /* Check if a moribund breakpoint explains the stop. */
for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
{
if (breakpoint_location_address_match (loc, aspace, bp_addr))
@@ -5653,6 +5618,7 @@ bpstat_what (bpstat bs_head)
break;
case bp_breakpoint:
case bp_hardware_breakpoint:
+ case bp_single_step:
case bp_until:
case bp_finish:
case bp_shlib_event:
@@ -6011,6 +5977,7 @@ bptype_string (enum bptype type)
{bp_none, "?deleted?"},
{bp_breakpoint, "breakpoint"},
{bp_hardware_breakpoint, "hw breakpoint"},
+ {bp_single_step, "sw single-step"},
{bp_until, "until"},
{bp_finish, "finish"},
{bp_watchpoint, "watchpoint"},
@@ -6202,6 +6169,7 @@ print_one_breakpoint_location (struct breakpoint *b,

case bp_breakpoint:
case bp_hardware_breakpoint:
+ case bp_single_step:
case bp_until:
case bp_finish:
case bp_longjmp:
@@ -7080,6 +7048,7 @@ init_bp_location (struct bp_location *loc, const struct bp_location_ops *ops,
switch (owner->type)
{
case bp_breakpoint:
+ case bp_single_step:
case bp_until:
case bp_finish:
case bp_longjmp:
@@ -9020,10 +8989,31 @@ enable_breakpoints_after_startup (void)
breakpoint_re_set ();
}

+/* Create a new single-step breakpoint for thread THREAD, with no
+ locations. */
+
+static struct breakpoint *
+new_single_step_breakpoint (int thread, struct gdbarch *gdbarch)
+{
+ struct breakpoint *b = XNEW (struct breakpoint);
+
+ init_raw_breakpoint_without_location (b, gdbarch, bp_single_step,
+ &momentary_breakpoint_ops);

-/* Set a breakpoint that will evaporate an end of command
- at address specified by SAL.
- Restrict it to frame FRAME if FRAME is nonzero. */
+ b->disposition = disp_donttouch;
+ b->frame_id = null_frame_id;
+
+ b->thread = thread;
+ gdb_assert (b->thread != 0);
+
+ add_to_breakpoint_chain (b);
+
+ return b;
+}
+
+/* Set a momentary breakpoint of type TYPE at address specified by
+ SAL. If FRAME_ID is valid, the breakpoint is restricted to that
+ frame. */

struct breakpoint *
set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
@@ -13129,45 +13119,13 @@ bkpt_re_set (struct breakpoint *b)
breakpoint_re_set_default (b);
}

-/* Copy SRC's shadow buffer and whatever else we'd set if we actually
- inserted DEST, so we can remove it later, in case SRC is removed
- first. */
-
-static void
-bp_target_info_copy_insertion_state (struct bp_target_info *dest,
- const struct bp_target_info *src)
-{
- dest->shadow_len = src->shadow_len;
- memcpy (dest->shadow_contents, src->shadow_contents, src->shadow_len);
- dest->placed_size = src->placed_size;
-}
-
static int
bkpt_insert_location (struct bp_location *bl)
{
if (bl->loc_type == bp_loc_hardware_breakpoint)
- return target_insert_hw_breakpoint (bl->gdbarch,
- &bl->target_info);
+ return target_insert_hw_breakpoint (bl->gdbarch, &bl->target_info);
else
- {
- struct bp_target_info *bp_tgt = &bl->target_info;
- int ret;
- int sss_slot;
-
- /* There is no need to insert a breakpoint if an unconditional
- raw/sss breakpoint is already inserted at that location. */
- sss_slot = find_single_step_breakpoint (bp_tgt->placed_address_space,
- bp_tgt->placed_address);
- if (sss_slot >= 0)
- {
- struct bp_target_info *sss_bp_tgt = single_step_breakpoints[sss_slot];
-
- bp_target_info_copy_insertion_state (bp_tgt, sss_bp_tgt);
- return 0;
- }
-
- return target_insert_breakpoint (bl->gdbarch, bp_tgt);
- }
+ return target_insert_breakpoint (bl->gdbarch, &bl->target_info);
}

static int
@@ -13176,19 +13134,7 @@ bkpt_remove_location (struct bp_location *bl)
if (bl->loc_type == bp_loc_hardware_breakpoint)
return target_remove_hw_breakpoint (bl->gdbarch, &bl->target_info);
else
- {
- struct bp_target_info *bp_tgt = &bl->target_info;
- struct address_space *aspace = bp_tgt->placed_address_space;
- CORE_ADDR address = bp_tgt->placed_address;
-
- /* Only remove the breakpoint if there is no raw/sss breakpoint
- still inserted at this location. Otherwise, we would be
- effectively disabling the raw/sss breakpoint. */
- if (single_step_breakpoint_inserted_here_p (aspace, address))
- return 0;
-
- return target_remove_breakpoint (bl->gdbarch, bp_tgt);
- }
+ return target_remove_breakpoint (bl->gdbarch, &bl->target_info);
}

static int
@@ -15186,217 +15132,73 @@ invalidate_bp_value_on_memory_change (struct inferior *inferior,
}
}

-/* Create and insert a raw software breakpoint at PC. Return an
- identifier, which should be used to remove the breakpoint later.
- In general, places which call this should be using something on the
- breakpoint chain instead; this function should be eliminated
- someday. */
-
-void *
-deprecated_insert_raw_breakpoint (struct gdbarch *gdbarch,
- struct address_space *aspace, CORE_ADDR pc)
-{
- struct bp_target_info *bp_tgt;
- struct bp_location *bl;
-
- bp_tgt = XCNEW (struct bp_target_info);
-
- bp_tgt->placed_address_space = aspace;
- bp_tgt->placed_address = pc;
-
- /* If an unconditional non-raw breakpoint is already inserted at
- that location, there's no need to insert another. However, with
- target-side evaluation of breakpoint conditions, if the
- breakpoint that is currently inserted on the target is
- conditional, we need to make it unconditional. Note that a
- breakpoint with target-side commands is not reported even if
- unconditional, so we need to remove the commands from the target
- as well. */
- bl = find_non_raw_software_breakpoint_inserted_here (aspace, pc);
- if (bl != NULL
- && VEC_empty (agent_expr_p, bl->target_info.conditions)
- && VEC_empty (agent_expr_p, bl->target_info.tcommands))
- {
- bp_target_info_copy_insertion_state (bp_tgt, &bl->target_info);
- return bp_tgt;
- }
-
- if (target_insert_breakpoint (gdbarch, bp_tgt) != 0)
- {
- /* Could not insert the breakpoint. */
- xfree (bp_tgt);
- return NULL;
- }
-
- return bp_tgt;
-}
-
-/* Remove a breakpoint BP inserted by
- deprecated_insert_raw_breakpoint. */
-
-int
-deprecated_remove_raw_breakpoint (struct gdbarch *gdbarch, void *bp)
-{
- struct bp_target_info *bp_tgt = bp;
- struct address_space *aspace = bp_tgt->placed_address_space;
- CORE_ADDR address = bp_tgt->placed_address;
- struct bp_location *bl;
- int ret;
-
- bl = find_non_raw_software_breakpoint_inserted_here (aspace, address);
-
- /* Only remove the raw breakpoint if there are no other non-raw
- breakpoints still inserted at this location. Otherwise, we would
- be effectively disabling those breakpoints. */
- if (bl == NULL)
- ret = target_remove_breakpoint (gdbarch, bp_tgt);
- else if (!VEC_empty (agent_expr_p, bl->target_info.conditions)
- || !VEC_empty (agent_expr_p, bl->target_info.tcommands))
- {
- /* The target is evaluating conditions, and when we inserted the
- software single-step breakpoint, we had made the breakpoint
- unconditional and command-less on the target side. Reinsert
- to restore the conditions/commands. */
- ret = target_insert_breakpoint (bl->gdbarch, &bl->target_info);
- }
- else
- ret = 0;
-
- xfree (bp_tgt);
-
- return ret;
-}
-
-/* Create and insert a breakpoint for software single step. */
+/* See breakpoint.h. */

void
insert_single_step_breakpoint (struct gdbarch *gdbarch,
struct address_space *aspace,
CORE_ADDR next_pc)
{
- void **bpt_p;
+ struct thread_info *tp = inferior_thread ();
+ struct symtab_and_line sal;
+ CORE_ADDR pc = next_pc;

- if (single_step_breakpoints[0] == NULL)
- {
- bpt_p = &single_step_breakpoints[0];
- single_step_gdbarch[0] = gdbarch;
- }
- else
+ if (tp->control.single_step_breakpoints == NULL)
{
- gdb_assert (single_step_breakpoints[1] == NULL);
- bpt_p = &single_step_breakpoints[1];
- single_step_gdbarch[1] = gdbarch;
+ tp->control.single_step_breakpoints
+ = new_single_step_breakpoint (tp->num, gdbarch);
}

- /* NOTE drow/2006-04-11: A future improvement to this function would
- be to only create the breakpoints once, and actually put them on
- the breakpoint chain. That would let us use set_raw_breakpoint.
- We could adjust the addresses each time they were needed. Doing
- this requires corresponding changes elsewhere where single step
- breakpoints are handled, however. So, for now, we use this. */
-
- *bpt_p = deprecated_insert_raw_breakpoint (gdbarch, aspace, next_pc);
- if (*bpt_p == NULL)
- error (_("Could not insert single-step breakpoint at %s"),
- paddress (gdbarch, next_pc));
-}
-
-/* Check if the breakpoints used for software single stepping
- were inserted or not. */
-
-int
-single_step_breakpoints_inserted (void)
-{
- return (single_step_breakpoints[0] != NULL
- || single_step_breakpoints[1] != NULL);
-}
-
-/* Remove and delete any breakpoints used for software single step. */
-
-void
-remove_single_step_breakpoints (void)
-{
- gdb_assert (single_step_breakpoints[0] != NULL);
+ sal = find_pc_line (pc, 0);
+ sal.pc = pc;
+ sal.section = find_pc_overlay (pc);
+ sal.explicit_pc = 1;
+ add_location_to_breakpoint (tp->control.single_step_breakpoints, &sal);

- /* See insert_single_step_breakpoint for more about this deprecated
- call. */
- deprecated_remove_raw_breakpoint (single_step_gdbarch[0],
- single_step_breakpoints[0]);
- single_step_gdbarch[0] = NULL;
- single_step_breakpoints[0] = NULL;
+ update_global_location_list_nothrow (1);

- if (single_step_breakpoints[1] != NULL)
- {
- deprecated_remove_raw_breakpoint (single_step_gdbarch[1],
- single_step_breakpoints[1]);
- single_step_gdbarch[1] = NULL;
- single_step_breakpoints[1] = NULL;
- }
+ /* update_global_location_list does not insert breakpoints when
+ always_inserted_mode is not enabled. Explicitly insert them
+ now. */
+ if (!breakpoints_always_inserted_mode ())
+ insert_breakpoint_locations ();
}

-/* Delete software single step breakpoints without removing them from
- the inferior. This is intended to be used if the inferior's address
- space where they were inserted is already gone, e.g. after exit or
- exec. */
+/* See breakpoint.h. */

-void
-cancel_single_step_breakpoints (void)
+int
+breakpoint_is_inserted_here (struct breakpoint *bp,
+ struct address_space *aspace, CORE_ADDR pc)
{
- int i;
-
- for (i = 0; i < 2; i++)
- if (single_step_breakpoints[i])
- {
- xfree (single_step_breakpoints[i]);
- single_step_breakpoints[i] = NULL;
- single_step_gdbarch[i] = NULL;
- }
-}
-
-/* Detach software single-step breakpoints from INFERIOR_PTID without
- removing them. */
+ struct bp_location *loc;

-static void
-detach_single_step_breakpoints (void)
-{
- int i;
+ for (loc = bp->loc; loc != NULL; loc = loc->next)
+ if (loc->inserted
+ && breakpoint_location_address_match (loc, aspace, pc))
+ return 1;

- for (i = 0; i < 2; i++)
- if (single_step_breakpoints[i])
- target_remove_breakpoint (single_step_gdbarch[i],
- single_step_breakpoints[i]);
+ return 0;
}

-/* Find the software single-step breakpoint that inserted at PC.
- Returns its slot if found, and -1 if not found. */
+/* See breakpoint.h. */

-static int
-find_single_step_breakpoint (struct address_space *aspace,
- CORE_ADDR pc)
+int
+single_step_breakpoint_inserted_here_p (struct address_space *aspace,
+ CORE_ADDR pc)
{
- int i;
+ struct breakpoint *bpt;

- for (i = 0; i < 2; i++)
+ ALL_BREAKPOINTS (bpt)
{
- struct bp_target_info *bp_tgt = single_step_breakpoints[i];
- if (bp_tgt
- && breakpoint_address_match (bp_tgt->placed_address_space,
- bp_tgt->placed_address,
- aspace, pc))
- return i;
- }
-
- return -1;
-}
+ struct bp_location *loc;

-/* Check whether a software single-step breakpoint is inserted at
- PC. */
+ if (bpt->type != bp_single_step)
+ continue;

-int
-single_step_breakpoint_inserted_here_p (struct address_space *aspace,
- CORE_ADDR pc)
-{
- return find_single_step_breakpoint (aspace, pc) >= 0;
+ if (breakpoint_is_inserted_here (bpt, aspace, pc))
+ return 1;
+ }
+ return 0;
}

/* Returns 0 if 'bp' is NOT a syscall catchpoint,
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index f6d06ce..5920181 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -47,18 +47,13 @@ struct linespec_sals;
\f

/* Type of breakpoint. */
-/* FIXME In the future, we should fold all other breakpoint-like
- things into here. This includes:
-
- * single-step (for machines where we have to simulate single
- stepping) (probably, though perhaps it is better for it to look as
- much as possible like a single-step to wait_for_inferior). */

enum bptype
{
bp_none = 0, /* Eventpoint has been deleted */
bp_breakpoint, /* Normal breakpoint */
bp_hardware_breakpoint, /* Hardware assisted breakpoint */
+ bp_single_step, /* Software single-step */
bp_until, /* used by until command */
bp_finish, /* used by finish command */
bp_watchpoint, /* Watchpoint */
@@ -1126,6 +1121,15 @@ extern int regular_breakpoint_inserted_here_p (struct address_space *,
extern int software_breakpoint_inserted_here_p (struct address_space *,
CORE_ADDR);

+/* Check whether any location of BP is inserted at PC. */
+
+extern int breakpoint_is_inserted_here (struct breakpoint *bp,
+ struct address_space *aspace,
+ CORE_ADDR pc);
+
+/* Check whether any software single-step breakpoint is inserted at
+ PC. */
+
extern int single_step_breakpoint_inserted_here_p (struct address_space *,
CORE_ADDR);

@@ -1442,22 +1446,13 @@ extern void add_solib_catchpoint (char *arg, int is_load, int is_temp,
deletes all breakpoints. */
extern void delete_command (char *arg, int from_tty);

-/* Manage a software single step breakpoint (or two). Insert may be
- called twice before remove is called. */
+/* Insert a new software single step breakpoint for the current
+ thread. Insert may be called multiple times; each time will add a
+ new location to the set of potential addresses the next instruction
+ is at. */
extern void insert_single_step_breakpoint (struct gdbarch *,
struct address_space *,
CORE_ADDR);
-extern int single_step_breakpoints_inserted (void);
-extern void remove_single_step_breakpoints (void);
-extern void cancel_single_step_breakpoints (void);
-
-/* Manage manual breakpoints, separate from the normal chain of
- breakpoints. These functions are used in murky target-specific
- ways. Please do not add more uses! */
-extern void *deprecated_insert_raw_breakpoint (struct gdbarch *,
- struct address_space *,
- CORE_ADDR);
-extern int deprecated_remove_raw_breakpoint (struct gdbarch *, void *);

/* Check if any hardware watchpoints have triggered, according to the
target. */
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 522b674..77bbbde 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -52,6 +52,13 @@ struct thread_control_state
/* Exception-resume breakpoint. */
struct breakpoint *exception_resume_breakpoint;

+ /* Breakpoints used for software single stepping. Plural, because
+ it may have multiple locations. E.g., if stepping over a
+ conditional branch instruction we can't decode the condition for,
+ we'll need to put a breakpoint at the branch destination, and
+ another at the instruction after the branch. */
+ struct breakpoint *single_step_breakpoints;
+
/* Range to single step within.

If this is nonzero, respond to a single-step signal by continuing
@@ -197,6 +204,11 @@ struct thread_info
/* Should we step over breakpoint next time keep_going is called? */
int stepping_over_breakpoint;

+ /* Should we step over a watchpoint next time keep_going is called?
+ This is needed on targets with non-continuable and non-steppable
+ watchpoints. */
+ int stepping_over_watchpoint;
+
/* Set to TRUE if we should finish single-stepping over a breakpoint
after hitting the current step-resume breakpoint. The context here
is that GDB is to do `next' or `step' while signal arrives.
@@ -280,6 +292,19 @@ extern void delete_step_resume_breakpoint (struct thread_info *);
/* Delete an exception_resume_breakpoint from the thread database. */
extern void delete_exception_resume_breakpoint (struct thread_info *);

+/* Delete the single-step breakpoints of thread TP, if any. */
+extern void delete_single_step_breakpoints (struct thread_info *tp);
+
+/* Check if the thread has software single stepping breakpoints
+ set. */
+extern int thread_has_single_step_breakpoints_set (struct thread_info *tp);
+
+/* Check whether the thread has software single stepping breakpoints
+ set at PC. */
+extern int thread_has_single_step_breakpoint_here (struct thread_info *tp,
+ struct address_space *aspace,
+ CORE_ADDR addr);
+
/* Translate the integer thread id (GDB's homegrown id, not the system's)
into a "pid" (which may be overloaded with extra thread information). */
extern ptid_t thread_id_to_pid (int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c18267f..2f3a848 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -373,8 +373,6 @@ static void context_switch (ptid_t ptid);

void init_thread_stepping_state (struct thread_info *tss);

-static void init_infwait_state (void);
-
static const char follow_fork_mode_child[] = "child";
static const char follow_fork_mode_parent[] = "parent";

@@ -844,6 +842,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
statement through an exec(). */
th->control.step_resume_breakpoint = NULL;
th->control.exception_resume_breakpoint = NULL;
+ th->control.single_step_breakpoints = NULL;
th->control.step_range_start = 0;
th->control.step_range_end = 0;

@@ -957,17 +956,6 @@ follow_exec (ptid_t pid, char *execd_pathname)
matically get reset there in the new process.). */
}

-/* Non-zero if we just simulating a single-step. This is needed
- because we cannot remove the breakpoints in the inferior process
- until after the `wait' in `wait_for_inferior'. */
-static int singlestep_breakpoints_inserted_p = 0;
-
-/* The thread we inserted single-step breakpoints for. */
-static ptid_t singlestep_ptid;
-
-/* PC when we started this single-step. */
-static CORE_ADDR singlestep_pc;
-
/* Info about an instruction that is being stepped over. Invalid if
ASPACE is NULL. */

@@ -978,6 +966,10 @@ struct step_over_info

/* The instruction's address. */
CORE_ADDR address;
+
+ /* The instruction being stepped over triggers a nonsteppable
+ watchpoint. */
+ int nonsteppable_watchpoint;
};

/* The step-over info of the location that is being stepped over.
@@ -1010,10 +1002,12 @@ static struct step_over_info step_over_info;
stepping over. */

static void
-set_step_over_info (struct address_space *aspace, CORE_ADDR address)
+set_step_over_info (struct address_space *aspace, CORE_ADDR address,
+ int nonsteppable_watchpoint)
{
step_over_info.aspace = aspace;
step_over_info.address = address;
+ step_over_info.nonsteppable_watchpoint = nonsteppable_watchpoint;
}

/* Called when we're not longer stepping over a breakpoint / an
@@ -1024,6 +1018,7 @@ clear_step_over_info (void)
{
step_over_info.aspace = NULL;
step_over_info.address = 0;
+ step_over_info.nonsteppable_watchpoint = 0;
}

/* See inferior.h. */
@@ -1032,10 +1027,35 @@ int
stepping_past_instruction_at (struct address_space *aspace,
CORE_ADDR address)
{
+ if (step_over_info.aspace != NULL
+ && breakpoint_address_match (aspace, address,
+ step_over_info.aspace,
+ step_over_info.address))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepping over breakpoint's address: %s\n",
+ paddress (target_gdbarch (), address));
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+stepping_past_nonsteppable_watchpoint (void)
+{
+ return step_over_info.nonsteppable_watchpoint;
+}
+
+/* Returns true if any breakpoint/watchpoint that would normally be
+ inserted can't be inserted due to run control. */
+
+static int
+any_breakpoint_lifted (void)
+{
return (step_over_info.aspace != NULL
- && breakpoint_address_match (aspace, address,
- step_over_info.aspace,
- step_over_info.address));
+ || stepping_past_nonsteppable_watchpoint ());
}

\f
@@ -1633,9 +1653,6 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
if (ptid_equal (inferior_ptid, old_ptid))
inferior_ptid = new_ptid;

- if (ptid_equal (singlestep_ptid, old_ptid))
- singlestep_ptid = new_ptid;
-
for (displaced = displaced_step_inferior_states;
displaced;
displaced = displaced->next)
@@ -1694,29 +1711,17 @@ set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c)
process. */
int sched_multi = 0;

-/* Try to setup for software single stepping over the specified location.
- Return 1 if target_resume() should use hardware single step.
-
- GDBARCH the current gdbarch.
- PC the location to step over. */
+/* Try to setup for software single stepping over the specified
+ location. Return 0 if target_resume() should use hardware single
+ step. GDBARCH is the current frame's gdbarch. PC is the location
+ to step over. */

static int
maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc)
{
- int hw_step = 1;
-
- if (execution_direction == EXEC_FORWARD
- && gdbarch_software_single_step_p (gdbarch)
- && gdbarch_software_single_step (gdbarch, get_current_frame ()))
- {
- hw_step = 0;
- /* Do not pull these breakpoints until after a `wait' in
- `wait_for_inferior'. */
- singlestep_breakpoints_inserted_p = 1;
- singlestep_ptid = inferior_ptid;
- singlestep_pc = pc;
- }
- return hw_step;
+ return (execution_direction == EXEC_FORWARD
+ && gdbarch_software_single_step_p (gdbarch)
+ && gdbarch_software_single_step (gdbarch, get_current_frame ()));
}

ptid_t
@@ -1739,8 +1744,7 @@ user_visible_resume_ptid (int step)
resume_ptid = inferior_ptid;
}
else if ((scheduler_mode == schedlock_on)
- || (scheduler_mode == schedlock_step
- && (step || singlestep_breakpoints_inserted_p)))
+ || (scheduler_mode == schedlock_step && step))
{
/* User-settable 'scheduler' mode requires solo thread resume. */
resume_ptid = inferior_ptid;
@@ -1843,8 +1847,7 @@ a command like `return' or `jump' to continue execution."));
event, displaced stepping breaks the vfork child similarly as single
step software breakpoint. */
if (use_displaced_stepping (gdbarch)
- && (tp->control.trap_expected
- || (step && gdbarch_software_single_step_p (gdbarch)))
+ && tp->control.trap_expected
&& sig == GDB_SIGNAL_0
&& !current_inferior ()->waiting_for_vfork_done)
{
@@ -1877,7 +1880,7 @@ a command like `return' or `jump' to continue execution."));

/* Do we need to do it the hard way, w/temp breakpoints? */
else if (step)
- step = maybe_software_singlestep (gdbarch, pc);
+ step = !maybe_software_singlestep (gdbarch, pc);

/* Currently, our software single-step implementation leads to different
results than hardware single-stepping in one situation: when stepping
@@ -1903,9 +1906,17 @@ a command like `return' or `jump' to continue execution."));
at the current address, deliver the signal without stepping, and
once we arrive back at the step-resume breakpoint, actually step
over the breakpoint we originally wanted to step over. */
- if (singlestep_breakpoints_inserted_p
- && tp->control.trap_expected && sig != GDB_SIGNAL_0)
+ if (thread_has_single_step_breakpoints_set (tp)
+ && sig != GDB_SIGNAL_0
+ && any_breakpoint_lifted ())
{
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: delivering signal with breakpoints lifted. "
+ "replacing sss bp and letting handler run\n");
+ }
+
/* If we have nested signals or a pending signal is delivered
immediately after a handler returns, might might already have
a step-resume breakpoint set on the earlier handler. We cannot
@@ -1917,8 +1928,7 @@ a command like `return' or `jump' to continue execution."));
tp->step_after_step_resume_breakpoint = 1;
}

- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
+ delete_single_step_breakpoints (tp);

clear_step_over_info ();
tp->control.trap_expected = 0;
@@ -1929,7 +1939,7 @@ a command like `return' or `jump' to continue execution."));
/* If STEP is set, it's a request to use hardware stepping
facilities. But in that case, we should never
use singlestep breakpoint. */
- gdb_assert (!(singlestep_breakpoints_inserted_p && step));
+ gdb_assert (!(thread_has_single_step_breakpoints_set (tp) && step));

/* Decide the set of threads to ask the target to resume. Start
by assuming everything will be resumed, than narrow the set
@@ -1945,7 +1955,7 @@ a command like `return' or `jump' to continue execution."));
set_running (resume_ptid, 1);

/* Maybe resume a single thread after all. */
- if ((step || singlestep_breakpoints_inserted_p)
+ if ((step || thread_has_single_step_breakpoints_set (tp))
&& tp->control.trap_expected)
{
/* We're allowing a thread to run past a breakpoint it has
@@ -1998,13 +2008,10 @@ a command like `return' or `jump' to continue execution."));
tp->suspend.stop_signal = GDB_SIGNAL_0;

/* Advise target which signals may be handled silently. If we have
- removed breakpoints because we are stepping over one (which can
- happen only if we are not using displaced stepping), we need to
- receive all signals to avoid accidentally skipping a breakpoint
- during execution of a signal handler. */
- if ((step || singlestep_breakpoints_inserted_p)
- && tp->control.trap_expected
- && !use_displaced_stepping (gdbarch))
+ removed breakpoints, we need to receive all signals to avoid
+ accidentally skipping a breakpoint during execution of a signal
+ handler. */
+ if (any_breakpoint_lifted ())
target_pass_signals (0, NULL);
else
target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
@@ -2310,7 +2317,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
struct regcache *regcache = get_current_regcache ();

set_step_over_info (get_regcache_aspace (regcache),
- regcache_read_pc (regcache));
+ regcache_read_pc (regcache), 0);
}
else
clear_step_over_info ();
@@ -2350,9 +2357,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
correctly when the inferior is stopped. */
tp->prev_pc = regcache_read_pc (get_current_regcache ());

- /* Reset to normal state. */
- init_infwait_state ();
-
/* Resume inferior. */
resume (tp->control.trap_expected || step || bpstat_should_step (),
tp->suspend.stop_signal);
@@ -2417,13 +2421,9 @@ init_wait_for_inferior (void)
target_last_wait_ptid = minus_one_ptid;

previous_inferior_ptid = inferior_ptid;
- init_infwait_state ();

/* Discard any skipped inlined frames. */
clear_inline_frame_state (minus_one_ptid);
-
- singlestep_ptid = null_ptid;
- singlestep_pc = 0;
}

\f
@@ -2438,9 +2438,6 @@ enum infwait_states
infwait_nonstep_watch_state
};

-/* The PTID we'll do a target_wait on.*/
-ptid_t waiton_ptid;
-
/* Current inferior wait state. */
static enum infwait_states infwait_state;

@@ -2460,11 +2457,6 @@ struct execution_control_state
const char *stop_func_name;
int wait_some_more;

- /* We were in infwait_step_watch_state or
- infwait_nonstep_watch_state state, and the thread reported an
- event. */
- int stepped_after_stopped_by_watchpoint;
-
/* True if the event thread hit the single-step breakpoint of
another thread. Thus the event doesn't cause a stop, the thread
needs to be single-stepped past the single-step breakpoint before
@@ -2609,6 +2601,7 @@ delete_step_resume_breakpoint_callback (struct thread_info *info, void *data)

delete_step_resume_breakpoint (info);
delete_exception_resume_breakpoint (info);
+ delete_single_step_breakpoints (info);
return 0;
}

@@ -2634,6 +2627,7 @@ delete_step_thread_step_resume_breakpoint (void)

delete_step_resume_breakpoint (tp);
delete_exception_resume_breakpoint (tp);
+ delete_single_step_breakpoints (tp);
}
else
/* In all-stop mode, delete all step-resume and longjmp-resume
@@ -2641,6 +2635,39 @@ delete_step_thread_step_resume_breakpoint (void)
iterate_over_threads (delete_step_resume_breakpoint_callback, NULL);
}

+static void
+delete_stopped_threads_single_step_breakpoints (void)
+{
+ if (!target_has_execution
+ || ptid_equal (inferior_ptid, null_ptid))
+ {
+ /* If the inferior has exited, we have already deleted the
+ single-step breakpoints out of GDB's lists. */
+ return;
+ }
+
+ if (non_stop)
+ {
+ /* If in non-stop mode, only delete the step-resume or
+ longjmp-resume breakpoint of the thread that just stopped
+ stepping. */
+ struct thread_info *tp = inferior_thread ();
+
+ delete_single_step_breakpoints (tp);
+ }
+ else
+ {
+ struct thread_info *tp;
+
+ ALL_NON_EXITED_THREADS (tp)
+ {
+ /* In all-stop mode, delete single-step breakpoints of any
+ thread that had them. */
+ delete_single_step_breakpoints (tp);
+ }
+ }
+}
+
/* A cleanup wrapper. */

static void
@@ -2789,6 +2816,7 @@ wait_for_inferior (void)
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
struct cleanup *old_chain;
+ ptid_t waiton_ptid = minus_one_ptid;

memset (ecs, 0, sizeof (*ecs));

@@ -2844,6 +2872,7 @@ fetch_inferior_event (void *client_data)
struct cleanup *ts_old_chain;
int was_sync = sync_execution;
int cmd_done = 0;
+ ptid_t waiton_ptid = minus_one_ptid;

memset (ecs, 0, sizeof (*ecs));

@@ -2961,6 +2990,7 @@ void
init_thread_stepping_state (struct thread_info *tss)
{
tss->stepping_over_breakpoint = 0;
+ tss->stepping_over_watchpoint = 0;
tss->step_after_step_resume_breakpoint = 0;
}

@@ -3120,7 +3150,7 @@ adjust_pc_after_break (struct execution_control_state *ecs)
software breakpoint. In this case (prev_pc == breakpoint_pc),
we also need to back up to the breakpoint address. */

- if (singlestep_breakpoints_inserted_p
+ if (thread_has_single_step_breakpoints_set (ecs->event_thread)
|| !ptid_equal (ecs->ptid, inferior_ptid)
|| !currently_stepping (ecs->event_thread)
|| ecs->event_thread->prev_pc == breakpoint_pc)
@@ -3130,13 +3160,6 @@ adjust_pc_after_break (struct execution_control_state *ecs)
}
}

-static void
-init_infwait_state (void)
-{
- waiton_ptid = pid_to_ptid (-1);
- infwait_state = infwait_normal_state;
-}
-
static int
stepped_in_from (struct frame_info *frame, struct frame_id step_frame_id)
{
@@ -3357,40 +3380,6 @@ handle_inferior_event (struct execution_control_state *ecs)
&& ecs->ws.kind != TARGET_WAITKIND_EXITED)
set_executing (ecs->ptid, 0);

- switch (infwait_state)
- {
- case infwait_normal_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
- break;
-
- case infwait_step_watch_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: infwait_step_watch_state\n");
-
- ecs->stepped_after_stopped_by_watchpoint = 1;
- break;
-
- case infwait_nonstep_watch_state:
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: infwait_nonstep_watch_state\n");
- insert_breakpoints ();
-
- /* FIXME-maybe: is this cleaner than setting a flag? Does it
- handle things like signals arriving and other things happening
- in combination correctly? */
- ecs->stepped_after_stopped_by_watchpoint = 1;
- break;
-
- default:
- internal_error (__FILE__, __LINE__, _("bad switch"));
- }
-
- infwait_state = infwait_normal_state;
- waiton_ptid = pid_to_ptid (-1);
-
switch (ecs->ws.kind)
{
case TARGET_WAITKIND_LOADED:
@@ -3548,8 +3537,6 @@ Cannot fill $_exitsignal with the correct signal number.\n"));

gdb_flush (gdb_stdout);
target_mourn_inferior ();
- singlestep_breakpoints_inserted_p = 0;
- cancel_single_step_breakpoints ();
stop_print_frame = 0;
stop_waiting (ecs);
return;
@@ -3642,12 +3629,7 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
detach_breakpoints (ecs->ws.value.related_pid);
}

- if (singlestep_breakpoints_inserted_p)
- {
- /* Pull the single step breakpoints out of the target. */
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
- }
+ delete_stopped_threads_single_step_breakpoints ();

/* In case the event is caught by a catchpoint, remember that
the event is to be followed at the next resume of the thread,
@@ -3734,9 +3716,6 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);

- singlestep_breakpoints_inserted_p = 0;
- cancel_single_step_breakpoints ();
-
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));

/* Do whatever is necessary to the parent branch of the vfork. */
@@ -3802,14 +3781,7 @@ Cannot fill $_exitsignal with the correct signal number.\n"));
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_NO_HISTORY\n");
/* Reverse execution: target ran out of history info. */

- /* Pull the single step breakpoints out of the target. */
- if (singlestep_breakpoints_inserted_p)
- {
- if (!ptid_equal (ecs->ptid, inferior_ptid))
- context_switch (ecs->ptid);
- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
- }
+ delete_stopped_threads_single_step_breakpoints ();
stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
observer_notify_no_history ();
stop_waiting (ecs);
@@ -3948,43 +3920,59 @@ handle_signal_stop (struct execution_control_state *ecs)
gdbarch = get_frame_arch (frame);

/* Pull the single step breakpoints out of the target. */
- if (singlestep_breakpoints_inserted_p)
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
+ && gdbarch_software_single_step_p (gdbarch))
{
+ struct regcache *regcache;
+ struct address_space *aspace;
+ CORE_ADDR pc;
+
+ regcache = get_thread_regcache (ecs->ptid);
+ aspace = get_regcache_aspace (regcache);
+ pc = regcache_read_pc (regcache);
+
/* However, before doing so, if this single-step breakpoint was
actually for another thread, set this thread up for moving
past it. */
- if (!ptid_equal (ecs->ptid, singlestep_ptid)
- && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
+ if (!thread_has_single_step_breakpoint_here (ecs->event_thread,
+ aspace, pc))
{
- struct regcache *regcache;
- struct address_space *aspace;
- CORE_ADDR pc;
-
- regcache = get_thread_regcache (ecs->ptid);
- aspace = get_regcache_aspace (regcache);
- pc = regcache_read_pc (regcache);
if (single_step_breakpoint_inserted_here_p (aspace, pc))
{
if (debug_infrun)
{
fprintf_unfiltered (gdb_stdlog,
- "infrun: [%s] hit step over single-step"
- " breakpoint of [%s]\n",
- target_pid_to_str (ecs->ptid),
- target_pid_to_str (singlestep_ptid));
+ "infrun: [%s] hit another thread's "
+ "single-step breakpoint\n",
+ target_pid_to_str (ecs->ptid));
}
ecs->hit_singlestep_breakpoint = 1;
}
}
+ else
+ {
+ if (debug_infrun)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: [%s] hit its "
+ "single-step breakpoint\n",
+ target_pid_to_str (ecs->ptid));
+ }
+ }

- remove_single_step_breakpoints ();
- singlestep_breakpoints_inserted_p = 0;
+ delete_stopped_threads_single_step_breakpoints ();
}

- if (ecs->stepped_after_stopped_by_watchpoint)
- stopped_by_watchpoint = 0;
+ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
+ && ecs->event_thread->control.trap_expected
+ && ecs->event_thread->stepping_over_watchpoint)
+ {
+ stopped_by_watchpoint = 0;
+ }
else
- stopped_by_watchpoint = watchpoints_triggered (&ecs->ws);
+ {
+ stopped_by_watchpoint = watchpoints_triggered (&ecs->ws);
+ }

/* If necessary, step over this watchpoint. We'll be back to display
it in a moment. */
@@ -4004,36 +3992,28 @@ handle_signal_stop (struct execution_control_state *ecs)
watchpoint expression. We do this by single-stepping the
target.

- It may not be necessary to disable the watchpoint to stop over
+ It may not be necessary to disable the watchpoint to step over
it. For example, the PA can (with some kernel cooperation)
single step over a watchpoint without disabling the watchpoint.

It is far more common to need to disable a watchpoint to step
the inferior over it. If we have non-steppable watchpoints,
we must disable the current watchpoint; it's simplest to
- disable all watchpoints and breakpoints. */
- int hw_step = 1;
-
- if (!target_have_steppable_watchpoint)
- {
- remove_breakpoints ();
- /* See comment in resume why we need to stop bypassing signals
- while breakpoints have been removed. */
- target_pass_signals (0, NULL);
- }
- /* Single step */
- hw_step = maybe_software_singlestep (gdbarch, stop_pc);
- target_resume (ecs->ptid, hw_step, GDB_SIGNAL_0);
- waiton_ptid = ecs->ptid;
- if (target_have_steppable_watchpoint)
- infwait_state = infwait_step_watch_state;
- else
- infwait_state = infwait_nonstep_watch_state;
- prepare_to_wait (ecs);
+ disable all watchpoints.
+
+ Any breakpoint at PC must also be stepped over -- if there's
+ one, it will have already triggered before the watchpoint
+ triggered, and we either already reported it to the user, or
+ it didn't cause a stop and we called keep_going. In either
+ case, if there was a breakpoint at PC, we must be trying to
+ step past it. */
+ ecs->event_thread->stepping_over_watchpoint = 1;
+ keep_going (ecs);
return;
}

ecs->event_thread->stepping_over_breakpoint = 0;
+ ecs->event_thread->stepping_over_watchpoint = 0;
bpstat_clear (&ecs->event_thread->control.stop_bpstat);
ecs->event_thread->control.stop_step = 0;
stop_print_frame = 1;
@@ -5311,15 +5291,21 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
- "infrun: expected thread advanced also\n");
+ "infrun: expected thread advanced also: prev_pc=%s, stop_pc=%s\n",
+ paddress (gdbarch, tp->prev_pc),
+ paddress (gdbarch, stop_pc));
+
+ /* Clear this before trying to insert the sss
+ breakpoint, in case we were previously trying to step
+ over this location in another thread, otherwise the
+ breakpoint ends up _not_ installed. It's what
+ keep_going would do too, if we called it. */
+ clear_step_over_info ();

insert_single_step_breakpoint (get_frame_arch (frame),
get_frame_address_space (frame),
stop_pc);
- singlestep_breakpoints_inserted_p = 1;
ecs->event_thread->control.trap_expected = 1;
- singlestep_ptid = inferior_ptid;
- singlestep_pc = stop_pc;

resume (0, GDB_SIGNAL_0);
prepare_to_wait (ecs);
@@ -5769,6 +5755,8 @@ keep_going (struct execution_control_state *ecs)
{
volatile struct gdb_exception e;
struct regcache *regcache = get_current_regcache ();
+ int remove_bp;
+ int remove_wps;

/* Either the trap was not expected, but we are continuing
anyway (if we got a signal, the user asked it be passed to
@@ -5788,13 +5776,19 @@ keep_going (struct execution_control_state *ecs)
(watchpoints, etc.) but the one we're stepping over, step one
instruction, and then re-insert the breakpoint when that step
is finished. */
- if ((ecs->hit_singlestep_breakpoint
- || thread_still_needs_step_over (ecs->event_thread))
- && !use_displaced_stepping (get_regcache_arch (regcache)))
+
+ remove_bp = (ecs->hit_singlestep_breakpoint
+ || thread_still_needs_step_over (ecs->event_thread));
+ remove_wps = (ecs->event_thread->stepping_over_watchpoint
+ && !target_have_steppable_watchpoint);
+
+ if (remove_bp && !use_displaced_stepping (get_regcache_arch (regcache)))
{
set_step_over_info (get_regcache_aspace (regcache),
- regcache_read_pc (regcache));
+ regcache_read_pc (regcache), remove_wps);
}
+ else if (remove_wps)
+ set_step_over_info (NULL, 0, remove_wps);
else
clear_step_over_info ();

@@ -5810,9 +5804,7 @@ keep_going (struct execution_control_state *ecs)
return;
}

- ecs->event_thread->control.trap_expected
- = (ecs->event_thread->stepping_over_breakpoint
- || ecs->hit_singlestep_breakpoint);
+ ecs->event_thread->control.trap_expected = (remove_bp || remove_wps);

/* Do not deliver GDB_SIGNAL_TRAP (except when the user
explicitly specifies that such a signal should be delivered
diff --git a/gdb/infrun.h b/gdb/infrun.h
index cc9cb33..13127ed 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -122,6 +122,10 @@ extern void follow_inferior_reset_breakpoints (void);
extern int stepping_past_instruction_at (struct address_space *aspace,
CORE_ADDR address);

+/* Returns true if we're trying to step past an instruction that
+ triggers a non-steppable watchpoint. */
+extern int stepping_past_nonsteppable_watchpoint (void);
+
extern void set_step_info (struct frame_info *frame,
struct symtab_and_line sal);

diff --git a/gdb/record-full.c b/gdb/record-full.c
index 119361f..a2257ed 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -961,7 +961,7 @@ record_full_resume (struct target_ops *ops, ptid_t ptid, int step,
else
{
/* This arch support soft sigle step. */
- if (single_step_breakpoints_inserted ())
+ if (thread_has_single_step_breakpoints_set (inferior_thread ()))
{
/* This is a soft single step. */
record_full_resume_step = 1;
@@ -1085,6 +1085,8 @@ record_full_wait_1 (struct target_ops *ops,

while (1)
{
+ struct thread_info *tp;
+
ret = ops->beneath->to_wait (ops->beneath, ptid, status, options);
if (status->kind == TARGET_WAITKIND_IGNORE)
{
@@ -1095,8 +1097,8 @@ record_full_wait_1 (struct target_ops *ops,
return ret;
}

- if (single_step_breakpoints_inserted ())
- remove_single_step_breakpoints ();
+ ALL_NON_EXITED_THREADS (tp)
+ delete_single_step_breakpoints (tp);

if (record_full_resume_step)
return ret;
diff --git a/gdb/thread.c b/gdb/thread.c
index 65890e1..1cdbaf4 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -85,26 +85,68 @@ inferior_thread (void)
return tp;
}

-void
-delete_step_resume_breakpoint (struct thread_info *tp)
+/* Delete the breakpoint pointed at by BP_P, if there's one. */
+
+static void
+delete_thread_breakpoint (struct breakpoint **bp_p)
{
- if (tp && tp->control.step_resume_breakpoint)
+ if (*bp_p != NULL)
{
- delete_breakpoint (tp->control.step_resume_breakpoint);
- tp->control.step_resume_breakpoint = NULL;
+ delete_breakpoint (*bp_p);
+ *bp_p = NULL;
}
}

void
+delete_step_resume_breakpoint (struct thread_info *tp)
+{
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.step_resume_breakpoint);
+}
+
+void
delete_exception_resume_breakpoint (struct thread_info *tp)
{
- if (tp && tp->control.exception_resume_breakpoint)
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.exception_resume_breakpoint);
+}
+
+void
+delete_single_step_breakpoints (struct thread_info *tp)
+{
+ if (tp != NULL)
+ delete_thread_breakpoint (&tp->control.single_step_breakpoints);
+}
+
+/* Delete the breakpoint pointed at by BP_P at the next stop, if
+ there's one. */
+
+static void
+delete_at_next_stop (struct breakpoint **bp)
+{
+ if (*bp != NULL)
{
- delete_breakpoint (tp->control.exception_resume_breakpoint);
- tp->control.exception_resume_breakpoint = NULL;
+ (*bp)->disposition = disp_del_at_next_stop;
+ *bp = NULL;
}
}

+int
+thread_has_single_step_breakpoints_set (struct thread_info *tp)
+{
+ return tp->control.single_step_breakpoints != NULL;
+}
+
+int
+thread_has_single_step_breakpoint_here (struct thread_info *tp,
+ struct address_space *aspace,
+ CORE_ADDR addr)
+{
+ return (tp->control.single_step_breakpoints != NULL
+ && breakpoint_is_inserted_here (tp->control.single_step_breakpoints,
+ aspace, addr));
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
@@ -112,18 +154,9 @@ clear_thread_inferior_resources (struct thread_info *tp)
but not any user-specified thread-specific breakpoints. We can not
delete the breakpoint straight-off, because the inferior might not
be stopped at the moment. */
- if (tp->control.step_resume_breakpoint)
- {
- tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
- tp->control.step_resume_breakpoint = NULL;
- }
-
- if (tp->control.exception_resume_breakpoint)
- {
- tp->control.exception_resume_breakpoint->disposition
- = disp_del_at_next_stop;
- tp->control.exception_resume_breakpoint = NULL;
- }
+ delete_at_next_stop (&tp->control.step_resume_breakpoint);
+ delete_at_next_stop (&tp->control.exception_resume_breakpoint);
+ delete_at_next_stop (&tp->control.single_step_breakpoints);

delete_longjmp_breakpoint_at_next_stop (tp->num);

--
1.9.3