diff options
-rw-r--r-- | gdb/ChangeLog | 17 | ||||
-rw-r--r-- | gdb/dcache.c | 100 | ||||
-rw-r--r-- | gdb/dcache.h | 15 | ||||
-rw-r--r-- | gdb/target.c | 53 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 8 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/breakpoint-shadow.exp | 38 |
6 files changed, 127 insertions, 104 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 8108e50..b9ac372 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,20 @@ +2014-03-05 Pedro Alves <palves@redhat.com> + + PR gdb/16575 + * dcache.c (dcache_poke_byte): Constify ptr parameter. Return + void. Update comment. + (dcache_xfer_memory): Delete. + (dcache_read_memory_partial): New, based on the read bits of + dcache_xfer_memory. + (dcache_update): Add status parameter. Use ULONGEST for len, and + adjust. Discard cache lines if the reason for the update was + error. + * dcache.h (dcache_xfer_memory): Delete declaration. + (dcache_read_memory_partial): New declaration. + (dcache_update): Update prototype. + * target.c (raw_memory_xfer_partial): Update the dcache here. + (memory_xfer_partial_1): Don't handle dcache writes here. + 2014-03-05 Mike Frysinger <vapier@gentoo.org> * remote-sim.c (gdbsim_load): Add const to prog. diff --git a/gdb/dcache.c b/gdb/dcache.c index ea51f5b..d3b546b 100644 --- a/gdb/dcache.c +++ b/gdb/dcache.c @@ -413,24 +413,20 @@ dcache_peek_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr) /* Write the byte at PTR into ADDR in the data cache. - The caller is responsible for also promptly writing the data - through to target memory. + The caller should have written the data through to target memory + already. - If addr is not in cache, this function does nothing; writing to - an area of memory which wasn't present in the cache doesn't cause - it to be loaded in. + If ADDR is not in cache, this function does nothing; writing to an + area of memory which wasn't present in the cache doesn't cause it + to be loaded in. */ - Always return 1 (meaning success) to simplify dcache_xfer_memory. */ - -static int -dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr) +static void +dcache_poke_byte (DCACHE *dcache, CORE_ADDR addr, const gdb_byte *ptr) { struct dcache_block *db = dcache_hit (dcache, addr); if (db) db->data[XFORM (dcache, addr)] = *ptr; - - return 1; } static int @@ -467,26 +463,17 @@ dcache_init (void) } -/* Read or write LEN bytes from inferior memory at MEMADDR, transferring - to or from debugger address MYADDR. Write to inferior if SHOULD_WRITE is - nonzero. +/* Read LEN bytes from dcache memory at MEMADDR, transferring to + debugger address MYADDR. If the data is presently cached, this + fills the cache. Arguments/return are like the target_xfer_partial + interface. */ - Return the number of bytes actually transfered, or -1 if the - transfer is not supported or otherwise fails. Return of a non-negative - value less than LEN indicates that no further transfer is possible. - NOTE: This is different than the to_xfer_partial interface, in which - positive values less than LEN mean further transfers may be possible. */ - -int -dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache, - CORE_ADDR memaddr, gdb_byte *myaddr, - int len, int should_write) +enum target_xfer_status +dcache_read_memory_partial (struct target_ops *ops, DCACHE *dcache, + CORE_ADDR memaddr, gdb_byte *myaddr, + ULONGEST len, ULONGEST *xfered_len) { - int i; - int res; - int (*xfunc) (DCACHE *dcache, CORE_ADDR addr, gdb_byte *ptr); - - xfunc = should_write ? dcache_poke_byte : dcache_peek_byte; + ULONGEST i; /* If this is a different inferior from what we've recorded, flush the cache. */ @@ -497,35 +484,27 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache, dcache->ptid = inferior_ptid; } - /* Do write-through first, so that if it fails, we don't write to - the cache at all. */ - - if (should_write) - { - res = target_write (ops, TARGET_OBJECT_RAW_MEMORY, - NULL, myaddr, memaddr, len); - if (res <= 0) - return res; - /* Update LEN to what was actually written. */ - len = res; - } - for (i = 0; i < len; i++) { - if (!xfunc (dcache, memaddr + i, myaddr + i)) + if (!dcache_peek_byte (dcache, memaddr + i, myaddr + i)) { /* That failed. Discard its cache line so we don't have a partially read line. */ dcache_invalidate_line (dcache, memaddr + i); - /* If we're writing, we still wrote LEN bytes. */ - if (should_write) - return len; - else - return i; + break; } } - - return len; + + if (i == 0) + { + /* FIXME: We lose the real error status. */ + return TARGET_XFER_E_IO; + } + else + { + *xfered_len = i; + return TARGET_XFER_OK; + } } /* FIXME: There would be some benefit to making the cache write-back and @@ -537,17 +516,26 @@ dcache_xfer_memory (struct target_ops *ops, DCACHE *dcache, "logically" connected but not actually a single call to one of the memory transfer functions. */ -/* Just update any cache lines which are already present. This is called - by memory_xfer_partial in cases where the access would otherwise not go - through the cache. */ +/* Just update any cache lines which are already present. This is + called by the target_xfer_partial machinery when writing raw + memory. */ void -dcache_update (DCACHE *dcache, CORE_ADDR memaddr, gdb_byte *myaddr, int len) +dcache_update (DCACHE *dcache, enum target_xfer_status status, + CORE_ADDR memaddr, const gdb_byte *myaddr, + ULONGEST len) { - int i; + ULONGEST i; for (i = 0; i < len; i++) - dcache_poke_byte (dcache, memaddr + i, myaddr + i); + if (status == TARGET_XFER_OK) + dcache_poke_byte (dcache, memaddr + i, myaddr + i); + else + { + /* Discard the whole cache line so we don't have a partially + valid line. */ + dcache_invalidate_line (dcache, memaddr + i); + } } /* Print DCACHE line INDEX. */ diff --git a/gdb/dcache.h b/gdb/dcache.h index 780dc30..020abd6 100644 --- a/gdb/dcache.h +++ b/gdb/dcache.h @@ -32,12 +32,13 @@ DCACHE *dcache_init (void); /* Free a DCACHE. */ void dcache_free (DCACHE *); -/* Simple to call from <remote>_xfer_memory. */ - -int dcache_xfer_memory (struct target_ops *ops, DCACHE *cache, CORE_ADDR mem, - gdb_byte *my, int len, int should_write); - -void dcache_update (DCACHE *dcache, CORE_ADDR memaddr, gdb_byte *myaddr, - int len); +enum target_xfer_status + dcache_read_memory_partial (struct target_ops *ops, DCACHE *dcache, + CORE_ADDR memaddr, gdb_byte *myaddr, + ULONGEST len, ULONGEST *xfered_len); + +void dcache_update (DCACHE *dcache, enum target_xfer_status status, + CORE_ADDR memaddr, const gdb_byte *myaddr, + ULONGEST len); #endif /* DCACHE_H */ diff --git a/gdb/target.c b/gdb/target.c index 6cd9741..f7868c0 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1074,6 +1074,23 @@ raw_memory_xfer_partial (struct target_ops *ops, gdb_byte *readbuf, } while (ops != NULL); + /* The cache works at the raw memory level. Make sure the cache + gets updated with raw contents no matter what kind of memory + object was originally being written. Note we do write-through + first, so that if it fails, we don't write to the cache contents + that never made it to the target. */ + if (writebuf != NULL + && !ptid_equal (inferior_ptid, null_ptid) + && target_dcache_init_p () + && (stack_cache_enabled_p () || code_cache_enabled_p ())) + { + DCACHE *dcache = target_dcache_get (); + + /* Note that writing to an area of memory which wasn't present + in the cache doesn't cause it to be loaded in. */ + dcache_update (dcache, res, memaddr, writebuf, *xfered_len); + } + return res; } @@ -1225,6 +1242,7 @@ memory_xfer_partial_1 (struct target_ops *ops, enum target_object object, inf = NULL; if (inf != NULL + && readbuf != NULL /* The dcache reads whole cache lines; that doesn't play well with reading from a trace buffer, because reading outside of the collected memory range fails. */ @@ -1234,23 +1252,9 @@ memory_xfer_partial_1 (struct target_ops *ops, enum target_object object, || (code_cache_enabled_p () && object == TARGET_OBJECT_CODE_MEMORY))) { DCACHE *dcache = target_dcache_get_or_init (); - int l; - if (readbuf != NULL) - l = dcache_xfer_memory (ops, dcache, memaddr, readbuf, reg_len, 0); - else - /* FIXME drow/2006-08-09: If we're going to preserve const - correctness dcache_xfer_memory should take readbuf and - writebuf. */ - l = dcache_xfer_memory (ops, dcache, memaddr, (void *) writebuf, - reg_len, 1); - if (l <= 0) - return TARGET_XFER_E_IO; - else - { - *xfered_len = (ULONGEST) l; - return TARGET_XFER_OK; - } + return dcache_read_memory_partial (ops, dcache, memaddr, readbuf, + reg_len, xfered_len); } /* If none of those methods found the memory we wanted, fall back @@ -1266,23 +1270,6 @@ memory_xfer_partial_1 (struct target_ops *ops, enum target_object object, res = raw_memory_xfer_partial (ops, readbuf, writebuf, memaddr, reg_len, xfered_len); - /* Make sure the cache gets updated no matter what - if we are writing - to the stack. Even if this write is not tagged as such, we still need - to update the cache. */ - - if (res == TARGET_XFER_OK - && inf != NULL - && writebuf != NULL - && target_dcache_init_p () - && !region->attrib.cache - && ((stack_cache_enabled_p () && object != TARGET_OBJECT_STACK_MEMORY) - || (code_cache_enabled_p () && object != TARGET_OBJECT_CODE_MEMORY))) - { - DCACHE *dcache = target_dcache_get (); - - dcache_update (dcache, memaddr, (void *) writebuf, reg_len); - } - /* If we still haven't got anything, return the last error. We give up. */ return res; diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 835338f..65b4f95 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2014-03-05 Pedro Alves <palves@redhat.com> + + PR gdb/16575 + * gdb.base/breakpoint-shadow.exp (compare_disassembly): New + procedure. + (top level): Adjust to use it. Add tests that exercise breakpoint + interaction with the code-cache. + 2014-02-26 Ludovic Courtès <ludo@gnu.org> * gdb.guile/scm-value.exp (test_value_in_inferior): Add diff --git a/gdb/testsuite/gdb.base/breakpoint-shadow.exp b/gdb/testsuite/gdb.base/breakpoint-shadow.exp index 74f7c8f..1414ec0 100644 --- a/gdb/testsuite/gdb.base/breakpoint-shadow.exp +++ b/gdb/testsuite/gdb.base/breakpoint-shadow.exp @@ -44,14 +44,36 @@ gdb_test_multiple "disass main" $test { gdb_test "b [gdb_get_line_number "break-first"]" "Breakpoint \[0-9\] at .*" "First breakpoint placed" gdb_test "b [gdb_get_line_number "break-second"]" "Breakpoint \[0-9\] at .*" "Second breakpoint placed" -set test "disassembly with breakpoints" -gdb_test_multiple "disass main" $test { - -re $match { - set got $expect_out(1,string) - if [string equal -nocase $orig $got] { - pass $test - } else { - fail $test +# Disassemble main, and compare the output to the original output +# before breakpoints were inserted. TEST is used as test message. + +proc test_disassembly {test} { + global match orig + + gdb_test_multiple "disass main" $test { + -re $match { + set got $expect_out(1,string) + if [string equal -nocase $orig $got] { + pass $test + } else { + fail $test + } } } } + +test_disassembly "disassembly with breakpoints" + +# Now check the interaction between the code cache and breakpoint +# always-inserted mode. + +# Recreate the code cache when breakpoints are already inserted. +gdb_test_no_output "set code-cache off" +gdb_test_no_output "set code-cache on" + +test_disassembly "disassembly with breakpoints, fresh code cache" + +# Delete breakpoints. This should update the code cache as well. +delete_breakpoints + +test_disassembly "disassembly without breakpoints, no stale breakpoints" |