gdb: Implemented watchpoint handling
authorJakob Gruber <jakob.gruber@gmail.com>
Fri, 13 Jul 2012 12:22:51 +0000 (14:22 +0200)
committerJakob Gruber <jakob.gruber@gmail.com>
Sun, 15 Jul 2012 21:26:45 +0000 (23:26 +0200)
Per data access, we never signal more than one watchpoint. Possible
scenarios are several watchpoints at the same address (for example
WATCH_ACCESS and WATCH_WRITE) or overlapping watchpoint ranges. This is
not completely correct.

simavr/sim/sim_core.c
simavr/sim/sim_gdb.c
simavr/sim/sim_gdb.h

index 5ceb7f3..f3106ef 100644 (file)
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include "sim_avr.h"
 #include "sim_core.h"
+#include "sim_gdb.h"
 #include "avr_flash.h"
 #include "avr_watchdog.h"
 
@@ -119,6 +120,11 @@ void avr_core_watch_write(avr_t *avr, uint16_t addr, uint8_t v)
                printf( FONT_RED "%04x : munching stack SP %04x, A=%04x <= %02x\n" FONT_DEFAULT, avr->pc, _avr_sp_get(avr), addr, v);
        }
 #endif
+
+       if (avr->gdb) {
+               avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_WRITE);
+       }
+
        avr->data[addr] = v;
 }
 
@@ -129,6 +135,11 @@ uint8_t avr_core_watch_read(avr_t *avr, uint16_t addr)
                                avr->pc, _avr_sp_get(avr), avr->flash[avr->pc + 1] | (avr->flash[avr->pc]<<8), addr, avr->ramend);
                CRASH();
        }
+
+       if (avr->gdb) {
+               avr_gdb_handle_watchpoints(avr, addr, AVR_GDB_WATCH_READ);
+       }
+
        return avr->data[addr];
 }
 
index 727b8ed..c4eb01b 100644 (file)
@@ -73,6 +73,22 @@ static int gdb_watch_find(const avr_gdb_watchpoints_t * w, uint32_t addr)
        return -1;
 }
 
+/**
+ * Contrary to gdb_watch_find, this actually checks the address against
+ * a watched memory _range_.
+ */
+static int gdb_watch_find_range(const avr_gdb_watchpoints_t * w, uint32_t addr)
+{
+       for (int i = 0; i < w->len; i++) {
+               if (w->points[i].addr <= addr &&
+                               addr < w->points[i].addr + w->points[i].size) {
+                       return i;
+               }
+       }
+
+       return -1;
+}
+
 /**
  * Returns -1 on error, 0 otherwise.
  */
@@ -444,6 +460,35 @@ static int gdb_network_handler(avr_gdb_t * g, uint32_t dosleep)
        return 1;
 }
 
+/**
+ * If an applicable watchpoint exists for addr, stop the cpu and send a status report.
+ * type is one of AVR_GDB_WATCH_READ, AVR_GDB_WATCH_WRITE depending on the type of access.
+ */
+void avr_gdb_handle_watchpoints(avr_t * avr, uint16_t addr, enum avr_gdb_watch_type type)
+{
+       avr_gdb_t *g = avr->gdb;
+
+       int i = gdb_watch_find_range(&g->watchpoints, addr);
+       if (i == -1) {
+               return;
+       }
+
+       int kind = g->watchpoints.points[i].kind;
+       if (kind & type) {
+               /* Send gdb reply (see GDB user manual appendix E.3). */
+               char cmd[78];
+               sprintf(cmd, "T%02x20:%02x;21:%02x%02x;22:%02x%02x%02x00;%s:%06x;",
+                               5, g->avr->data[R_SREG],
+                               g->avr->data[R_SPL], g->avr->data[R_SPH],
+                               g->avr->pc & 0xff, (g->avr->pc>>8)&0xff, (g->avr->pc>>16)&0xff,
+                               kind & AVR_GDB_WATCH_ACCESS ? "awatch" : kind & AVR_GDB_WATCH_WRITE ? "watch" : "rwatch",
+                               addr | 0x800000);
+               gdb_send_reply(g, cmd);
+
+               avr->state = cpu_Stopped;
+       }
+}
+
 int avr_gdb_processor(avr_t * avr, int sleep)
 {
        if (!avr || !avr->gdb)
index a9e8aa4..2c672c3 100644 (file)
@@ -42,6 +42,9 @@ int avr_gdb_init(avr_t * avr);
 // call from the main AVR decoder thread
 int avr_gdb_processor(avr_t * avr, int sleep);
 
+// Called from sim_core.c
+void avr_gdb_handle_watchpoints(avr_t * g, uint16_t addr, enum avr_gdb_watch_type type);
+
 #ifdef __cplusplus
 };
 #endif