/* Memory breakpoint operations for the remote server for GDB.
- Copyright (C) 2002, 2003, 2005, 2007, 2008, 2009
+ Copyright (C) 2002, 2003, 2005, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
Contributed by MontaVista Software.
#define MAX_BREAKPOINT_LEN 8
-struct breakpoint
+/* GDB will never try to install multiple breakpoints at the same
+ address. But, we need to keep track of internal breakpoints too,
+ and so we do need to be able to install multiple breakpoints at the
+ same address transparently. We keep track of two different, and
+ closely related structures. A raw breakpoint, which manages the
+ low level, close to the metal aspect of a breakpoint. It holds the
+ breakpoint address, and a buffer holding a copy of the instructions
+ that would be in memory had not been a breakpoint there (we call
+ that the shadow memory of the breakpoint). We occasionally need to
+ temporarilly uninsert a breakpoint without the client knowing about
+ it (e.g., to step over an internal breakpoint), so we keep an
+ `inserted' state associated with this low level breakpoint
+ structure. There can only be one such object for a given address.
+ Then, we have (a bit higher level) breakpoints. This structure
+ holds a callback to be called whenever a breakpoint is hit, a
+ high-level type, and a link to a low level raw breakpoint. There
+ can be many high-level breakpoints at the same address, and all of
+ them will point to the same raw breakpoint, which is reference
+ counted. */
+
+/* The low level, physical, raw breakpoint. */
+struct raw_breakpoint
{
- struct breakpoint *next;
+ struct raw_breakpoint *next;
+
+ /* A reference count. Each high level breakpoint referencing this
+ raw breakpoint accounts for one reference. */
+ int refcount;
+
+ /* The breakpoint's insertion address. There can only be one raw
+ breakpoint for a given PC. */
CORE_ADDR pc;
+
+ /* The breakpoint's shadow memory. */
unsigned char old_data[MAX_BREAKPOINT_LEN];
- /* Non-zero iff we are stepping over this breakpoint. */
- int reinserting;
+ /* Non-zero if this breakpoint is currently inserted in the
+ inferior. */
+ int inserted;
+
+ /* Non-zero if this breakpoint is currently disabled because we no
+ longer detect it as inserted. */
+ int shlib_disabled;
+};
+
+/* The type of a breakpoint. */
+enum bkpt_type
+ {
+ /* A GDB breakpoint, requested with a Z0 packet. */
+ gdb_breakpoint,
+
+ /* A basic-software-single-step breakpoint. */
+ reinsert_breakpoint,
+
+ /* Any other breakpoint type that doesn't require specific
+ treatment goes here. E.g., an event breakpoint. */
+ other_breakpoint,
+ };
+
+/* A high level (in gdbserver's perspective) breakpoint. */
+struct breakpoint
+{
+ struct breakpoint *next;
- /* Non-NULL iff this breakpoint was inserted to step over
- another one. Points to the other breakpoint (which is also
- in the *next chain somewhere). */
- struct breakpoint *breakpoint_to_reinsert;
+ /* The breakpoint's type. */
+ enum bkpt_type type;
+
+ /* Link to this breakpoint's raw breakpoint. This is always
+ non-NULL. */
+ struct raw_breakpoint *raw;
/* Function to call when we hit this breakpoint. If it returns 1,
- the breakpoint will be deleted; 0, it will be reinserted for
- another round. */
+ the breakpoint shall be deleted; 0 or if this callback is NULL,
+ it will be left inserted. */
int (*handler) (CORE_ADDR);
};
+static struct raw_breakpoint *
+find_raw_breakpoint_at (CORE_ADDR where)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->pc == where)
+ return bp;
+
+ return NULL;
+}
+
+static struct raw_breakpoint *
+set_raw_breakpoint_at (CORE_ADDR where)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+ int err;
+
+ if (breakpoint_data == NULL)
+ error ("Target does not support breakpoints.");
+
+ bp = find_raw_breakpoint_at (where);
+ if (bp != NULL)
+ {
+ bp->refcount++;
+ return bp;
+ }
+
+ bp = xcalloc (1, sizeof (*bp));
+ bp->pc = where;
+ bp->refcount = 1;
+
+ /* Note that there can be fast tracepoint jumps installed in the
+ same memory range, so to get at the original memory, we need to
+ use read_inferior_memory, which masks those out. */
+ err = read_inferior_memory (where, bp->old_data, breakpoint_len);
+ if (err != 0)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to read shadow memory of"
+ " breakpoint at 0x%s (%s).\n",
+ paddress (where), strerror (err));
+ free (bp);
+ return NULL;
+ }
+
+ err = (*the_target->write_memory) (where, breakpoint_data,
+ breakpoint_len);
+ if (err != 0)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to insert breakpoint at 0x%s (%s).\n",
+ paddress (where), strerror (err));
+ free (bp);
+ return NULL;
+ }
+
+ /* Link the breakpoint in. */
+ bp->inserted = 1;
+ bp->next = proc->raw_breakpoints;
+ proc->raw_breakpoints = bp;
+ return bp;
+}
+
+/* Notice that breakpoint traps are always installed on top of fast
+ tracepoint jumps. This is even if the fast tracepoint is installed
+ at a later time compared to when the breakpoint was installed.
+ This means that a stopping breakpoint or tracepoint has higher
+ "priority". In turn, this allows having fast and slow tracepoints
+ (and breakpoints) at the same address behave correctly. */
+
+
+/* A fast tracepoint jump. */
+
+struct fast_tracepoint_jump
+{
+ struct fast_tracepoint_jump *next;
+
+ /* A reference count. GDB can install more than one fast tracepoint
+ at the same address (each with its own action list, for
+ example). */
+ int refcount;
+
+ /* The fast tracepoint's insertion address. There can only be one
+ of these for a given PC. */
+ CORE_ADDR pc;
+
+ /* Non-zero if this fast tracepoint jump is currently inserted in
+ the inferior. */
+ int inserted;
+
+ /* The length of the jump instruction. */
+ int length;
+
+ /* A poor-man's flexible array member, holding both the jump
+ instruction to insert, and a copy of the instruction that would
+ be in memory had not been a jump there (the shadow memory of the
+ tracepoint jump). */
+ unsigned char insn_and_shadow[0];
+};
+
+/* Fast tracepoint FP's jump instruction to insert. */
+#define fast_tracepoint_jump_insn(fp) \
+ ((fp)->insn_and_shadow + 0)
+
+/* The shadow memory of fast tracepoint jump FP. */
+#define fast_tracepoint_jump_shadow(fp) \
+ ((fp)->insn_and_shadow + (fp)->length)
+
+
+/* Return the fast tracepoint jump set at WHERE. */
+
+static struct fast_tracepoint_jump *
+find_fast_tracepoint_jump_at (CORE_ADDR where)
+{
+ struct process_info *proc = current_process ();
+ struct fast_tracepoint_jump *jp;
+
+ for (jp = proc->fast_tracepoint_jumps; jp != NULL; jp = jp->next)
+ if (jp->pc == where)
+ return jp;
+
+ return NULL;
+}
+
+int
+fast_tracepoint_jump_here (CORE_ADDR where)
+{
+ struct fast_tracepoint_jump *jp = find_fast_tracepoint_jump_at (where);
+
+ return (jp != NULL);
+}
+
+int
+delete_fast_tracepoint_jump (struct fast_tracepoint_jump *todel)
+{
+ struct fast_tracepoint_jump *bp, **bp_link;
+ int ret;
+ struct process_info *proc = current_process ();
+
+ bp = proc->fast_tracepoint_jumps;
+ bp_link = &proc->fast_tracepoint_jumps;
+
+ while (bp)
+ {
+ if (bp == todel)
+ {
+ if (--bp->refcount == 0)
+ {
+ struct fast_tracepoint_jump *prev_bp_link = *bp_link;
+
+ /* Unlink it. */
+ *bp_link = bp->next;
+
+ /* Since there can be breakpoints inserted in the same
+ address range, we use `write_inferior_memory', which
+ takes care of layering breakpoints on top of fast
+ tracepoints, and on top of the buffer we pass it.
+ This works because we've already unlinked the fast
+ tracepoint jump above. Also note that we need to
+ pass the current shadow contents, because
+ write_inferior_memory updates any shadow memory with
+ what we pass here, and we want that to be a nop. */
+ ret = write_inferior_memory (bp->pc,
+ fast_tracepoint_jump_shadow (bp),
+ bp->length);
+ if (ret != 0)
+ {
+ /* Something went wrong, relink the jump. */
+ *bp_link = prev_bp_link;
+
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to uninsert fast tracepoint jump "
+ "at 0x%s (%s) while deleting it.\n",
+ paddress (bp->pc), strerror (ret));
+ return ret;
+ }
+
+ free (bp);
+ }
+
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+
+ warning ("Could not find fast tracepoint jump in list.");
+ return ENOENT;
+}
+
+struct fast_tracepoint_jump *
+set_fast_tracepoint_jump (CORE_ADDR where,
+ unsigned char *insn, ULONGEST length)
+{
+ struct process_info *proc = current_process ();
+ struct fast_tracepoint_jump *jp;
+ int err;
+
+ /* We refcount fast tracepoint jumps. Check if we already know
+ about a jump at this address. */
+ jp = find_fast_tracepoint_jump_at (where);
+ if (jp != NULL)
+ {
+ jp->refcount++;
+ return jp;
+ }
+
+ /* We don't, so create a new object. Double the length, because the
+ flexible array member holds both the jump insn, and the
+ shadow. */
+ jp = xcalloc (1, sizeof (*jp) + (length * 2));
+ jp->pc = where;
+ jp->length = length;
+ memcpy (fast_tracepoint_jump_insn (jp), insn, length);
+ jp->refcount = 1;
+
+ /* Note that there can be trap breakpoints inserted in the same
+ address range. To access the original memory contents, we use
+ `read_inferior_memory', which masks out breakpoints. */
+ err = read_inferior_memory (where,
+ fast_tracepoint_jump_shadow (jp), jp->length);
+ if (err != 0)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to read shadow memory of"
+ " fast tracepoint at 0x%s (%s).\n",
+ paddress (where), strerror (err));
+ free (jp);
+ return NULL;
+ }
+
+ /* Link the jump in. */
+ jp->inserted = 1;
+ jp->next = proc->fast_tracepoint_jumps;
+ proc->fast_tracepoint_jumps = jp;
+
+ /* Since there can be trap breakpoints inserted in the same address
+ range, we use use `write_inferior_memory', which takes care of
+ layering breakpoints on top of fast tracepoints, on top of the
+ buffer we pass it. This works because we've already linked in
+ the fast tracepoint jump above. Also note that we need to pass
+ the current shadow contents, because write_inferior_memory
+ updates any shadow memory with what we pass here, and we want
+ that to be a nop. */
+ err = write_inferior_memory (where, fast_tracepoint_jump_shadow (jp),
+ length);
+ if (err != 0)
+ {
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to insert fast tracepoint jump at 0x%s (%s).\n",
+ paddress (where), strerror (err));
+
+ /* Unlink it. */
+ proc->fast_tracepoint_jumps = jp->next;
+ free (jp);
+
+ return NULL;
+ }
+
+ return jp;
+}
+
void
+uninsert_fast_tracepoint_jumps_at (CORE_ADDR pc)
+{
+ struct fast_tracepoint_jump *jp;
+ int err;
+
+ jp = find_fast_tracepoint_jump_at (pc);
+ if (jp == NULL)
+ {
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ fprintf (stderr,
+ "Could not find fast tracepoint jump at 0x%s "
+ "in list (uninserting).\n",
+ paddress (pc));
+ return;
+ }
+
+ if (jp->inserted)
+ {
+ jp->inserted = 0;
+
+ /* Since there can be trap breakpoints inserted in the same
+ address range, we use use `write_inferior_memory', which
+ takes care of layering breakpoints on top of fast
+ tracepoints, and on top of the buffer we pass it. This works
+ because we've already marked the fast tracepoint fast
+ tracepoint jump uninserted above. Also note that we need to
+ pass the current shadow contents, because
+ write_inferior_memory updates any shadow memory with what we
+ pass here, and we want that to be a nop. */
+ err = write_inferior_memory (jp->pc,
+ fast_tracepoint_jump_shadow (jp),
+ jp->length);
+ if (err != 0)
+ {
+ jp->inserted = 1;
+
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to uninsert fast tracepoint jump at 0x%s (%s).\n",
+ paddress (pc), strerror (err));
+ }
+ }
+}
+
+void
+reinsert_fast_tracepoint_jumps_at (CORE_ADDR where)
+{
+ struct fast_tracepoint_jump *jp;
+ int err;
+
+ jp = find_fast_tracepoint_jump_at (where);
+ if (jp == NULL)
+ {
+ /* This can happen when we remove breakpoints when a tracepoint
+ hit causes a tracing stop, while handling a step-over. */
+ if (debug_threads)
+ fprintf (stderr,
+ "Could not find fast tracepoint jump at 0x%s "
+ "in list (reinserting).\n",
+ paddress (where));
+ return;
+ }
+
+ if (jp->inserted)
+ error ("Jump already inserted at reinsert time.");
+
+ jp->inserted = 1;
+
+ /* Since there can be trap breakpoints inserted in the same address
+ range, we use `write_inferior_memory', which takes care of
+ layering breakpoints on top of fast tracepoints, and on top of
+ the buffer we pass it. This works because we've already marked
+ the fast tracepoint jump inserted above. Also note that we need
+ to pass the current shadow contents, because
+ write_inferior_memory updates any shadow memory with what we pass
+ here, and we want that to be a nop. */
+ err = write_inferior_memory (where,
+ fast_tracepoint_jump_shadow (jp), jp->length);
+ if (err != 0)
+ {
+ jp->inserted = 0;
+
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to reinsert fast tracepoint jump at 0x%s (%s).\n",
+ paddress (where), strerror (err));
+ }
+}
+
+struct breakpoint *
set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR))
{
struct process_info *proc = current_process ();
struct breakpoint *bp;
+ struct raw_breakpoint *raw;
- if (breakpoint_data == NULL)
- error ("Target does not support breakpoints.");
+ raw = set_raw_breakpoint_at (where);
- bp = xmalloc (sizeof (struct breakpoint));
- memset (bp, 0, sizeof (struct breakpoint));
+ if (raw == NULL)
+ {
+ /* warn? */
+ return NULL;
+ }
- (*the_target->read_memory) (where, bp->old_data,
- breakpoint_len);
- (*the_target->write_memory) (where, breakpoint_data,
- breakpoint_len);
+ bp = xcalloc (1, sizeof (struct breakpoint));
+ bp->type = other_breakpoint;
- bp->pc = where;
+ bp->raw = raw;
bp->handler = handler;
bp->next = proc->breakpoints;
proc->breakpoints = bp;
+
+ return bp;
}
-static void
-delete_breakpoint (struct breakpoint *bp)
+static int
+delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel)
{
- struct process_info *proc = current_process ();
- struct breakpoint *cur;
+ struct raw_breakpoint *bp, **bp_link;
+ int ret;
- if (proc->breakpoints == bp)
+ bp = proc->raw_breakpoints;
+ bp_link = &proc->raw_breakpoints;
+
+ while (bp)
{
- proc->breakpoints = bp->next;
- (*the_target->write_memory) (bp->pc, bp->old_data,
- breakpoint_len);
- free (bp);
- return;
+ if (bp == todel)
+ {
+ if (bp->inserted)
+ {
+ struct raw_breakpoint *prev_bp_link = *bp_link;
+
+ *bp_link = bp->next;
+
+ /* Since there can be trap breakpoints inserted in the
+ same address range, we use `write_inferior_memory',
+ which takes care of layering breakpoints on top of
+ fast tracepoints, and on top of the buffer we pass
+ it. This works because we've already unlinked the
+ fast tracepoint jump above. Also note that we need
+ to pass the current shadow contents, because
+ write_inferior_memory updates any shadow memory with
+ what we pass here, and we want that to be a nop. */
+ ret = write_inferior_memory (bp->pc, bp->old_data,
+ breakpoint_len);
+ if (ret != 0)
+ {
+ /* Something went wrong, relink the breakpoint. */
+ *bp_link = prev_bp_link;
+
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to uninsert raw breakpoint "
+ "at 0x%s (%s) while deleting it.\n",
+ paddress (bp->pc), strerror (ret));
+ return ret;
+ }
+
+ }
+ else
+ *bp_link = bp->next;
+
+ free (bp);
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
}
- cur = proc->breakpoints;
- while (cur->next)
+
+ warning ("Could not find raw breakpoint in list.");
+ return ENOENT;
+}
+
+static int
+release_breakpoint (struct process_info *proc, struct breakpoint *bp)
+{
+ int newrefcount;
+ int ret;
+
+ newrefcount = bp->raw->refcount - 1;
+ if (newrefcount == 0)
{
- if (cur->next == bp)
+ ret = delete_raw_breakpoint (proc, bp->raw);
+ if (ret != 0)
+ return ret;
+ }
+ else
+ bp->raw->refcount = newrefcount;
+
+ free (bp);
+
+ return 0;
+}
+
+static int
+delete_breakpoint_1 (struct process_info *proc, struct breakpoint *todel)
+{
+ struct breakpoint *bp, **bp_link;
+ int err;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ if (bp == todel)
{
- cur->next = bp->next;
- (*the_target->write_memory) (bp->pc, bp->old_data,
- breakpoint_len);
- free (bp);
- return;
+ *bp_link = bp->next;
+
+ err = release_breakpoint (proc, bp);
+ if (err != 0)
+ return err;
+
+ bp = *bp_link;
+ return 0;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
}
}
+
warning ("Could not find breakpoint in list.");
+ return ENOENT;
+}
+
+int
+delete_breakpoint (struct breakpoint *todel)
+{
+ struct process_info *proc = current_process ();
+ return delete_breakpoint_1 (proc, todel);
}
static struct breakpoint *
-find_breakpoint_at (CORE_ADDR where)
+find_gdb_breakpoint_at (CORE_ADDR where)
{
struct process_info *proc = current_process ();
- struct breakpoint *bp = proc->breakpoints;
+ struct breakpoint *bp;
- while (bp != NULL)
- {
- if (bp->pc == where)
- return bp;
- bp = bp->next;
- }
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ if (bp->type == gdb_breakpoint && bp->raw->pc == where)
+ return bp;
return NULL;
}
-void
-delete_breakpoint_at (CORE_ADDR addr)
+int
+set_gdb_breakpoint_at (CORE_ADDR where)
{
- struct breakpoint *bp = find_breakpoint_at (addr);
+ struct breakpoint *bp;
+
+ if (breakpoint_data == NULL)
+ return 1;
+
+ /* If we see GDB inserting a second breakpoint at the same address,
+ then the first breakpoint must have disappeared due to a shared
+ library unload. On targets where the shared libraries are
+ handled by userspace, like SVR4, for example, GDBserver can't
+ tell if a library was loaded or unloaded. Since we refcount
+ breakpoints, if we didn't do this, we'd just increase the
+ refcount of the previous breakpoint at this address, but the trap
+ was not planted in the inferior anymore, thus the breakpoint
+ would never be hit. */
+ bp = find_gdb_breakpoint_at (where);
if (bp != NULL)
- delete_breakpoint (bp);
+ {
+ delete_gdb_breakpoint_at (where);
+
+ /* Might as well validate all other breakpoints. */
+ validate_breakpoints ();
+ }
+
+ bp = set_breakpoint_at (where, NULL);
+ if (bp == NULL)
+ return -1;
+
+ bp->type = gdb_breakpoint;
+ return 0;
}
-static int
-reinsert_breakpoint_handler (CORE_ADDR stop_pc)
+int
+delete_gdb_breakpoint_at (CORE_ADDR addr)
{
- struct breakpoint *stop_bp, *orig_bp;
+ struct breakpoint *bp;
+ int err;
- stop_bp = find_breakpoint_at (stop_pc);
- if (stop_bp == NULL)
- error ("lost the stopping breakpoint.");
+ if (breakpoint_data == NULL)
+ return 1;
- orig_bp = stop_bp->breakpoint_to_reinsert;
- if (orig_bp == NULL)
- error ("no breakpoint to reinsert");
+ bp = find_gdb_breakpoint_at (addr);
+ if (bp == NULL)
+ return -1;
- (*the_target->write_memory) (orig_bp->pc, breakpoint_data,
- breakpoint_len);
- orig_bp->reinserting = 0;
- return 1;
+ err = delete_breakpoint (bp);
+ if (err)
+ return -1;
+
+ return 0;
+}
+
+int
+gdb_breakpoint_here (CORE_ADDR where)
+{
+ struct breakpoint *bp = find_gdb_breakpoint_at (where);
+
+ return (bp != NULL);
}
void
-reinsert_breakpoint_by_bp (CORE_ADDR stop_pc, CORE_ADDR stop_at)
+set_reinsert_breakpoint (CORE_ADDR stop_at)
{
- struct breakpoint *bp, *orig_bp;
+ struct breakpoint *bp;
- orig_bp = find_breakpoint_at (stop_pc);
- if (orig_bp == NULL)
- error ("Could not find original breakpoint in list.");
+ bp = set_breakpoint_at (stop_at, NULL);
+ bp->type = reinsert_breakpoint;
+}
- set_breakpoint_at (stop_at, reinsert_breakpoint_handler);
+void
+delete_reinsert_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp, **bp_link;
- bp = find_breakpoint_at (stop_at);
- if (bp == NULL)
- error ("Could not find breakpoint in list (reinserting by breakpoint).");
- bp->breakpoint_to_reinsert = orig_bp;
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
+ {
+ if (bp->type == reinsert_breakpoint)
+ {
+ *bp_link = bp->next;
+ release_breakpoint (proc, bp);
+ bp = *bp_link;
+ }
+ else
+ {
+ bp_link = &bp->next;
+ bp = *bp_link;
+ }
+ }
+}
+
+static void
+uninsert_raw_breakpoint (struct raw_breakpoint *bp)
+{
+ if (bp->inserted)
+ {
+ int err;
+
+ bp->inserted = 0;
+ /* Since there can be fast tracepoint jumps inserted in the same
+ address range, we use `write_inferior_memory', which takes
+ care of layering breakpoints on top of fast tracepoints, and
+ on top of the buffer we pass it. This works because we've
+ already unlinked the fast tracepoint jump above. Also note
+ that we need to pass the current shadow contents, because
+ write_inferior_memory updates any shadow memory with what we
+ pass here, and we want that to be a nop. */
+ err = write_inferior_memory (bp->pc, bp->old_data,
+ breakpoint_len);
+ if (err != 0)
+ {
+ bp->inserted = 1;
- (*the_target->write_memory) (orig_bp->pc, orig_bp->old_data,
- breakpoint_len);
- orig_bp->reinserting = 1;
+ if (debug_threads)
+ fprintf (stderr,
+ "Failed to uninsert raw breakpoint at 0x%s (%s).\n",
+ paddress (bp->pc), strerror (err));
+ }
+ }
}
void
-uninsert_breakpoint (CORE_ADDR stopped_at)
+uninsert_breakpoints_at (CORE_ADDR pc)
{
- struct breakpoint *bp;
+ struct raw_breakpoint *bp;
- bp = find_breakpoint_at (stopped_at);
+ bp = find_raw_breakpoint_at (pc);
if (bp == NULL)
- error ("Could not find breakpoint in list (uninserting).");
+ {
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ fprintf (stderr,
+ "Could not find breakpoint at 0x%s "
+ "in list (uninserting).\n",
+ paddress (pc));
+ return;
+ }
- (*the_target->write_memory) (bp->pc, bp->old_data,
- breakpoint_len);
- bp->reinserting = 1;
+ if (bp->inserted)
+ uninsert_raw_breakpoint (bp);
}
void
-reinsert_breakpoint (CORE_ADDR stopped_at)
+uninsert_all_breakpoints (void)
{
- struct breakpoint *bp;
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
- bp = find_breakpoint_at (stopped_at);
- if (bp == NULL)
- error ("Could not find breakpoint in list (uninserting).");
- if (! bp->reinserting)
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (bp->inserted)
+ uninsert_raw_breakpoint (bp);
+}
+
+static void
+reinsert_raw_breakpoint (struct raw_breakpoint *bp)
+{
+ int err;
+
+ if (bp->inserted)
error ("Breakpoint already inserted at reinsert time.");
- (*the_target->write_memory) (bp->pc, breakpoint_data,
- breakpoint_len);
- bp->reinserting = 0;
+ err = (*the_target->write_memory) (bp->pc, breakpoint_data,
+ breakpoint_len);
+ if (err == 0)
+ bp->inserted = 1;
+ else if (debug_threads)
+ fprintf (stderr,
+ "Failed to reinsert breakpoint at 0x%s (%s).\n",
+ paddress (bp->pc), strerror (err));
}
-int
-check_breakpoints (CORE_ADDR stop_pc)
+void
+reinsert_breakpoints_at (CORE_ADDR pc)
{
- struct breakpoint *bp;
+ struct raw_breakpoint *bp;
- bp = find_breakpoint_at (stop_pc);
+ bp = find_raw_breakpoint_at (pc);
if (bp == NULL)
- return 0;
- if (bp->reinserting)
{
- warning ("Hit a removed breakpoint?");
- return 0;
+ /* This can happen when we remove all breakpoints while handling
+ a step-over. */
+ if (debug_threads)
+ fprintf (stderr,
+ "Could not find raw breakpoint at 0x%s "
+ "in list (reinserting).\n",
+ paddress (pc));
+ return;
}
- if ((*bp->handler) (bp->pc))
+ reinsert_raw_breakpoint (bp);
+}
+
+void
+reinsert_all_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct raw_breakpoint *bp;
+
+ for (bp = proc->raw_breakpoints; bp != NULL; bp = bp->next)
+ if (!bp->inserted)
+ reinsert_raw_breakpoint (bp);
+}
+
+void
+check_breakpoints (CORE_ADDR stop_pc)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp, **bp_link;
+
+ bp = proc->breakpoints;
+ bp_link = &proc->breakpoints;
+
+ while (bp)
{
- delete_breakpoint (bp);
- return 2;
+ if (bp->raw->pc == stop_pc)
+ {
+ if (!bp->raw->inserted)
+ {
+ warning ("Hit a removed breakpoint?");
+ return;
+ }
+
+ if (bp->handler != NULL && (*bp->handler) (stop_pc))
+ {
+ *bp_link = bp->next;
+
+ release_breakpoint (proc, bp);
+
+ bp = *bp_link;
+ continue;
+ }
+ }
+
+ bp_link = &bp->next;
+ bp = *bp_link;
}
- else
- return 1;
}
void
breakpoint_len = bp_len;
}
+int
+breakpoint_here (CORE_ADDR addr)
+{
+ return (find_raw_breakpoint_at (addr) != NULL);
+}
+
+int
+breakpoint_inserted_here (CORE_ADDR addr)
+{
+ struct raw_breakpoint *bp;
+
+ bp = find_raw_breakpoint_at (addr);
+
+ return (bp != NULL && bp->inserted);
+}
+
+static int
+validate_inserted_breakpoint (struct raw_breakpoint *bp)
+{
+ unsigned char *buf;
+ int err;
+
+ gdb_assert (bp->inserted);
+
+ buf = alloca (breakpoint_len);
+ err = (*the_target->read_memory) (bp->pc, buf, breakpoint_len);
+ if (err || memcmp (buf, breakpoint_data, breakpoint_len) != 0)
+ {
+ /* Tag it as gone. */
+ bp->inserted = 0;
+ bp->shlib_disabled = 1;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+delete_disabled_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp, *next;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = next)
+ {
+ next = bp->next;
+ if (bp->raw->shlib_disabled)
+ delete_breakpoint_1 (proc, bp);
+ }
+}
+
+/* Check if breakpoints we inserted still appear to be inserted. They
+ may disappear due to a shared library unload, and worse, a new
+ shared library may be reloaded at the same address as the
+ previously unloaded one. If that happens, we should make sure that
+ the shadow memory of the old breakpoints isn't used when reading or
+ writing memory. */
+
+void
+validate_breakpoints (void)
+{
+ struct process_info *proc = current_process ();
+ struct breakpoint *bp;
+
+ for (bp = proc->breakpoints; bp != NULL; bp = bp->next)
+ {
+ if (bp->raw->inserted)
+ validate_inserted_breakpoint (bp->raw);
+ }
+
+ delete_disabled_breakpoints ();
+}
+
void
check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
{
struct process_info *proc = current_process ();
- struct breakpoint *bp = proc->breakpoints;
+ struct raw_breakpoint *bp = proc->raw_breakpoints;
+ struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
CORE_ADDR mem_end = mem_addr + mem_len;
+ int disabled_one = 0;
+
+ for (; jp != NULL; jp = jp->next)
+ {
+ CORE_ADDR bp_end = jp->pc + jp->length;
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ if (mem_addr >= bp_end)
+ continue;
+ if (jp->pc >= mem_end)
+ continue;
+
+ start = jp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = bp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - jp->pc;
+ buf_offset = start - mem_addr;
+
+ if (jp->inserted)
+ memcpy (buf + buf_offset,
+ fast_tracepoint_jump_shadow (jp) + copy_offset,
+ copy_len);
+ }
for (; bp != NULL; bp = bp->next)
{
copy_offset = start - bp->pc;
buf_offset = start - mem_addr;
- memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
+ if (bp->inserted)
+ {
+ if (validate_inserted_breakpoint (bp))
+ memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len);
+ else
+ disabled_one = 1;
+ }
}
+
+ if (disabled_one)
+ delete_disabled_breakpoints ();
}
void
check_mem_write (CORE_ADDR mem_addr, unsigned char *buf, int mem_len)
{
struct process_info *proc = current_process ();
- struct breakpoint *bp = proc->breakpoints;
+ struct raw_breakpoint *bp = proc->raw_breakpoints;
+ struct fast_tracepoint_jump *jp = proc->fast_tracepoint_jumps;
CORE_ADDR mem_end = mem_addr + mem_len;
+ int disabled_one = 0;
+
+ /* First fast tracepoint jumps, then breakpoint traps on top. */
+
+ for (; jp != NULL; jp = jp->next)
+ {
+ CORE_ADDR jp_end = jp->pc + jp->length;
+ CORE_ADDR start, end;
+ int copy_offset, copy_len, buf_offset;
+
+ if (mem_addr >= jp_end)
+ continue;
+ if (jp->pc >= mem_end)
+ continue;
+
+ start = jp->pc;
+ if (mem_addr > start)
+ start = mem_addr;
+
+ end = jp_end;
+ if (end > mem_end)
+ end = mem_end;
+
+ copy_len = end - start;
+ copy_offset = start - jp->pc;
+ buf_offset = start - mem_addr;
+
+ memcpy (fast_tracepoint_jump_shadow (jp) + copy_offset,
+ buf + buf_offset, copy_len);
+ if (jp->inserted)
+ memcpy (buf + buf_offset,
+ fast_tracepoint_jump_insn (jp) + copy_offset, copy_len);
+ }
for (; bp != NULL; bp = bp->next)
{
buf_offset = start - mem_addr;
memcpy (bp->old_data + copy_offset, buf + buf_offset, copy_len);
- if (bp->reinserting == 0)
- memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
+ if (bp->inserted)
+ {
+ if (validate_inserted_breakpoint (bp))
+ memcpy (buf + buf_offset, breakpoint_data + copy_offset, copy_len);
+ else
+ disabled_one = 1;
+ }
}
+
+ if (disabled_one)
+ delete_disabled_breakpoints ();
}
/* Delete all breakpoints, and un-insert them from the inferior. */
struct process_info *proc = current_process ();
while (proc->breakpoints)
- delete_breakpoint (proc->breakpoints);
+ delete_breakpoint_1 (proc, proc->breakpoints);
+}
+
+/* Clear the "inserted" flag in all breakpoints. */
+
+void
+mark_breakpoints_out (struct process_info *proc)
+{
+ struct raw_breakpoint *raw_bp;
+
+ for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
+ raw_bp->inserted = 0;
}
/* Release all breakpoints, but do not try to un-insert them from the
void
free_all_breakpoints (struct process_info *proc)
{
- struct breakpoint *bp;
+ mark_breakpoints_out (proc);
+ /* Note: use PROC explicitly instead of deferring to
+ delete_all_breakpoints --- CURRENT_INFERIOR may already have been
+ released when we get here. There should be no call to
+ current_process from here on. */
while (proc->breakpoints)
- {
- bp = proc->breakpoints;
- proc->breakpoints = bp->next;
- free (bp);
- }
+ delete_breakpoint_1 (proc, proc->breakpoints);
}