diff options
author | Pedro Alves <palves@redhat.com> | 2010-04-01 14:25:34 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2010-04-01 14:25:34 +0000 |
commit | 8b07ae33f0743a5dbad03cb4a76987f6db7fc38c (patch) | |
tree | c4fd50f0088748c74d0af003b576db1ea1f8fe3b /gdb/gdbserver/mem-break.c | |
parent | 86b17b608f08cee1e23221f28d79f9dc47352976 (diff) | |
download | gdb-8b07ae33f0743a5dbad03cb4a76987f6db7fc38c.zip gdb-8b07ae33f0743a5dbad03cb4a76987f6db7fc38c.tar.gz gdb-8b07ae33f0743a5dbad03cb4a76987f6db7fc38c.tar.bz2 |
* linux-low.c (linux_wait_1): Avoid setting need_step_over is
there's a GDB breakpoint at stop_pc. Always report a trap to GDB
if we could tell there's a GDB breakpoint at stop_pc.
(need_step_over_p): Don't do a step over if we find a GDB
breakpoint at the resume PC.
* mem-break.c (struct raw_breakpoint): New.
(enum bkpt_type): New type `gdb_breakpoint'.
(struct breakpoint): Delete the `PC', `old_data' and `inserted'
fields. New field `raw'.
(find_raw_breakpoint_at): New.
(set_raw_breakpoint_at): Handle refcounting. Create a raw
breakpoint instead.
(set_breakpoint_at): Adjust.
(delete_raw_breakpoint): New.
(release_breakpoint): New.
(delete_breakpoint): Rename to...
(delete_breakpoint_1): ... this. Add proc parameter. Use
release_breakpoint. Return ENOENT.
(delete_breakpoint): Reimplement.
(find_breakpoint_at): Delete.
(find_gdb_breakpoint_at): New.
(delete_breakpoint_at): Delete.
(set_gdb_breakpoint_at): New.
(delete_gdb_breakpoint_at): New.
(gdb_breakpoint_here): New.
(set_reinsert_breakpoint): Use release_breakpoint.
(uninsert_breakpoint): Rename to ...
(uninsert_raw_breakpoint): ... this.
(uninsert_breakpoints_at): Adjust to handle raw breakpoints.
(reinsert_raw_breakpoint): Change parameter type to
raw_breakpoint.
(reinsert_breakpoints_at): Adjust to handle raw breakpoints
instead.
(check_breakpoints): Adjust. Use release_breakpoint.
(breakpoint_here): Rewrite using find_raw_breakpoint_at.
(breakpoint_inserted_here): Ditto.
(check_mem_read): Adjust to iterate over raw breakpoints instead.
Don't trust the breakpoint's shadow if it is not inserted.
(check_mem_write): Adjust to iterate over raw breakpoints instead.
(delete_all_breakpoints): Adjust.
(free_all_breakpoints): Mark all breakpoints as uninserted, and
use delete_breakpoint_1.
* mem-break.h (breakpoints_supported): Delete declaration.
(set_gdb_breakpoint_at): Declare.
(gdb_breakpoint_here): Declare.
(delete_breakpoint_at): Delete.
(delete_gdb_breakpoint_at): Declare.
* server.h (struct raw_breakpoint): Forward declare.
(struct process_info): New field `raw_breakpoints'.
* linux-x86-low.c (x86_insert_point, x86_remote_point): Handle Z0
breakpoints.
Diffstat (limited to 'gdb/gdbserver/mem-break.c')
-rw-r--r-- | gdb/gdbserver/mem-break.c | 322 |
1 files changed, 246 insertions, 76 deletions
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c index 33de80c..5256cb7 100644 --- a/gdb/gdbserver/mem-break.c +++ b/gdb/gdbserver/mem-break.c @@ -26,9 +26,53 @@ int breakpoint_len; #define MAX_BREAKPOINT_LEN 8 +/* 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 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 if this breakpoint is currently inserted in the + inferior. */ + int inserted; +}; + /* 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, @@ -37,38 +81,57 @@ enum bkpt_type other_breakpoint, }; +/* A high level (in gdbserver's perspective) breakpoint. */ struct breakpoint { struct breakpoint *next; - CORE_ADDR pc; - unsigned char old_data[MAX_BREAKPOINT_LEN]; - - /* Non-zero if this breakpoint is currently inserted in the - inferior. */ - int inserted; /* 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 shall be deleted; 0, it will be left inserted. */ + the breakpoint shall be deleted; 0 or if this callback is NULL, + it will be left inserted. */ int (*handler) (CORE_ADDR); }; -static void uninsert_breakpoint (struct breakpoint *bp); +static struct raw_breakpoint * +find_raw_breakpoint_at (CORE_ADDR where) +{ + struct process_info *proc = current_process (); + struct raw_breakpoint *bp; -static struct breakpoint * + 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 breakpoint *bp; + 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; err = (*the_target->read_memory) (where, bp->old_data, breakpoint_len); @@ -97,8 +160,8 @@ set_raw_breakpoint_at (CORE_ADDR where) /* Link the breakpoint in. */ bp->inserted = 1; - bp->next = proc->breakpoints; - proc->breakpoints = bp; + bp->next = proc->raw_breakpoints; + proc->raw_breakpoints = bp; return bp; } @@ -107,10 +170,11 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) { struct process_info *proc = current_process (); struct breakpoint *bp; + struct raw_breakpoint *raw; - bp = set_raw_breakpoint_at (where); + raw = set_raw_breakpoint_at (where); - if (bp == NULL) + if (raw == NULL) { /* warn? */ return NULL; @@ -118,6 +182,8 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) bp = xcalloc (1, sizeof (struct breakpoint)); bp->type = other_breakpoint; + + bp->raw = raw; bp->handler = handler; bp->next = proc->breakpoints; @@ -126,11 +192,84 @@ set_breakpoint_at (CORE_ADDR where, int (*handler) (CORE_ADDR)) return bp; } -static void -delete_breakpoint (struct breakpoint *todel) +static int +delete_raw_breakpoint (struct process_info *proc, struct raw_breakpoint *todel) +{ + struct raw_breakpoint *bp, **bp_link; + int ret; + + bp = proc->raw_breakpoints; + bp_link = &proc->raw_breakpoints; + + while (bp) + { + if (bp == todel) + { + if (bp->inserted) + { + struct raw_breakpoint *prev_bp_link = *bp_link; + + *bp_link = bp->next; + + ret = (*the_target->write_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; + } + } + + 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) + { + 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 process_info *proc = current_process (); struct breakpoint *bp, **bp_link; + int err; bp = proc->breakpoints; bp_link = &proc->breakpoints; @@ -141,9 +280,12 @@ delete_breakpoint (struct breakpoint *todel) { *bp_link = bp->next; - uninsert_breakpoint (bp); - free (bp); - return; + err = release_breakpoint (proc, bp); + if (err != 0) + return err; + + bp = *bp_link; + return 0; } else { @@ -153,30 +295,71 @@ delete_breakpoint (struct breakpoint *todel) } warning ("Could not find breakpoint in list."); + return ENOENT; +} + +static 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); - if (bp != NULL) - delete_breakpoint (bp); + struct breakpoint *bp; + + if (breakpoint_data == NULL) + return 1; + + bp = set_breakpoint_at (where, NULL); + if (bp == NULL) + return -1; + + bp->type = gdb_breakpoint; + return 0; +} + +int +delete_gdb_breakpoint_at (CORE_ADDR addr) +{ + struct breakpoint *bp; + int err; + + if (breakpoint_data == NULL) + return 1; + + bp = find_gdb_breakpoint_at (addr); + if (bp == NULL) + 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 @@ -185,7 +368,6 @@ set_reinsert_breakpoint (CORE_ADDR stop_at) struct breakpoint *bp; bp = set_breakpoint_at (stop_at, NULL); - bp->type = reinsert_breakpoint; } @@ -203,13 +385,7 @@ delete_reinsert_breakpoints (void) if (bp->type == reinsert_breakpoint) { *bp_link = bp->next; - - /* If something goes wrong, maybe this is a shared library - breakpoint, and the shared library has been unmapped. - Assume the breakpoint is gone anyway. */ - uninsert_breakpoint (bp); - free (bp); - + release_breakpoint (proc, bp); bp = *bp_link; } else @@ -221,7 +397,7 @@ delete_reinsert_breakpoints (void) } static void -uninsert_breakpoint (struct breakpoint *bp) +uninsert_raw_breakpoint (struct raw_breakpoint *bp) { if (bp->inserted) { @@ -245,9 +421,9 @@ uninsert_breakpoint (struct breakpoint *bp) void uninsert_breakpoints_at (CORE_ADDR pc) { - struct breakpoint *bp; + struct raw_breakpoint *bp; - bp = find_breakpoint_at (pc); + bp = find_raw_breakpoint_at (pc); if (bp == NULL) { /* This can happen when we remove all breakpoints while handling @@ -261,11 +437,11 @@ uninsert_breakpoints_at (CORE_ADDR pc) } if (bp->inserted) - uninsert_breakpoint (bp); + uninsert_raw_breakpoint (bp); } static void -reinsert_raw_breakpoint (struct breakpoint *bp) +reinsert_raw_breakpoint (struct raw_breakpoint *bp) { int err; @@ -285,16 +461,16 @@ reinsert_raw_breakpoint (struct breakpoint *bp) void reinsert_breakpoints_at (CORE_ADDR pc) { - struct breakpoint *bp; + struct raw_breakpoint *bp; - bp = find_breakpoint_at (pc); + bp = find_raw_breakpoint_at (pc); if (bp == NULL) { /* 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 " + "Could not find raw breakpoint at 0x%s " "in list (reinserting).\n", paddress (pc)); return; @@ -314,9 +490,9 @@ check_breakpoints (CORE_ADDR stop_pc) while (bp) { - if (bp->pc == stop_pc) + if (bp->raw->pc == stop_pc) { - if (!bp->inserted) + if (!bp->raw->inserted) { warning ("Hit a removed breakpoint?"); return; @@ -326,7 +502,7 @@ check_breakpoints (CORE_ADDR stop_pc) { *bp_link = bp->next; - delete_breakpoint (bp); + release_breakpoint (proc, bp); bp = *bp_link; continue; @@ -348,34 +524,24 @@ set_breakpoint_data (const unsigned char *bp_data, int bp_len) int breakpoint_here (CORE_ADDR addr) { - struct process_info *proc = current_process (); - struct breakpoint *bp; - - for (bp = proc->breakpoints; bp != NULL; bp = bp->next) - if (bp->pc == addr) - return 1; - - return 0; + return (find_raw_breakpoint_at (addr) != NULL); } int breakpoint_inserted_here (CORE_ADDR addr) { - struct process_info *proc = current_process (); - struct breakpoint *bp; + struct raw_breakpoint *bp; - for (bp = proc->breakpoints; bp != NULL; bp = bp->next) - if (bp->pc == addr && bp->inserted) - return 1; + bp = find_raw_breakpoint_at (addr); - return 0; + return (bp != NULL && bp->inserted); } 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; CORE_ADDR mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) @@ -401,7 +567,8 @@ check_mem_read (CORE_ADDR mem_addr, unsigned char *buf, int mem_len) copy_offset = start - bp->pc; buf_offset = start - mem_addr; - memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); + if (bp->inserted) + memcpy (buf + buf_offset, bp->old_data + copy_offset, copy_len); } } @@ -409,7 +576,7 @@ 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; CORE_ADDR mem_end = mem_addr + mem_len; for (; bp != NULL; bp = bp->next) @@ -449,7 +616,7 @@ delete_all_breakpoints (void) struct process_info *proc = current_process (); while (proc->breakpoints) - delete_breakpoint (proc->breakpoints); + delete_breakpoint_1 (proc, proc->breakpoints); } /* Release all breakpoints, but do not try to un-insert them from the @@ -458,12 +625,15 @@ delete_all_breakpoints (void) void free_all_breakpoints (struct process_info *proc) { - struct breakpoint *bp; + struct raw_breakpoint *raw_bp; + for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next) + raw_bp->inserted = 0; + + /* 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); } |