Make hardware watchpoints work for process record.
authorPedro Alves <palves@redhat.com>
Sun, 22 Nov 2009 15:38:59 +0000 (15:38 +0000)
committerPedro Alves <palves@redhat.com>
Sun, 22 Nov 2009 15:38:59 +0000 (15:38 +0000)
        * breakpoint.c (hardware_watchpoint_inserted_in_range): New.
* breakpoint.h (hardware_watchpoint_inserted_in_range): Declare.
* record.c (record_beneath_to_stopped_by_watchpoint)
(record_beneath_to_stopped_data_address, record_hw_watchpoint):
New globals.
(record_exec_insn): Check for watchpoint hits.
(tmp_to_stopped_by_watchpoint, tmp_to_stopped_data_address): New
globals.
(record_open): Set tmp_to_stopped_by_watchpoint,
tmp_to_stopped_data_address,
record_beneath_to_stopped_by_watchpoint and
record_beneath_to_stopped_data_address.
(record_wait): Report watchpoint hits to the core.  Update and
extend comments.
(record_stopped_by_watchpoint): New.
(record_stopped_data_address): New.
(init_record_ops): Install them.
(init_record_core_ops): Ditto.

gdb/ChangeLog
gdb/breakpoint.c
gdb/breakpoint.h
gdb/record.c

index 25f84be26477379b60d59d7b8360dfe5d59cace8..bbf02db2177e590bb0a9bc3cef3c09baceb12ca7 100644 (file)
@@ -1,3 +1,27 @@
+2009-11-22  Pedro Alves  <pedro@codesourcery.com>
+           Michael Snyder  <msnyder@vmware.com>
+
+        Make hardware watchpoints work for process record.
+
+        * breakpoint.c (hardware_watchpoint_inserted_in_range): New.
+       * breakpoint.h (hardware_watchpoint_inserted_in_range): Declare.
+       * record.c (record_beneath_to_stopped_by_watchpoint)
+       (record_beneath_to_stopped_data_address, record_hw_watchpoint):
+       New globals.
+       (record_exec_insn): Check for watchpoint hits.
+       (tmp_to_stopped_by_watchpoint, tmp_to_stopped_data_address): New
+       globals.
+       (record_open): Set tmp_to_stopped_by_watchpoint,
+       tmp_to_stopped_data_address,
+       record_beneath_to_stopped_by_watchpoint and
+       record_beneath_to_stopped_data_address.
+       (record_wait): Report watchpoint hits to the core.  Update and
+       extend comments.
+       (record_stopped_by_watchpoint): New.
+       (record_stopped_data_address): New.
+       (init_record_ops): Install them.
+       (init_record_core_ops): Ditto.
+
 2009-11-21  Pedro Alves  <pedro@codesourcery.com>
 
        * breakpoint.c (update_watchpoint): Skip creating locations and
index fb6b9486ef6912ae6e6070522c5ced068db713f7..90d9247b361ef2db3e881b9b55dddb618246ce7c 100644 (file)
@@ -2380,6 +2380,38 @@ software_breakpoint_inserted_here_p (struct address_space *aspace, CORE_ADDR pc)
   return 0;
 }
 
+int
+hardware_watchpoint_inserted_in_range (struct address_space *aspace,
+                                      CORE_ADDR addr, ULONGEST len)
+{
+  struct breakpoint *bpt;
+
+  ALL_BREAKPOINTS (bpt)
+    {
+      struct bp_location *loc;
+
+      if (bpt->type != bp_hardware_watchpoint
+         && bpt->type != bp_access_watchpoint)
+       continue;
+
+      if (!breakpoint_enabled (bpt))
+       continue;
+
+      for (loc = bpt->loc; loc; loc = loc->next)
+       if (loc->pspace->aspace == aspace && loc->inserted)
+         {
+           CORE_ADDR l, h;
+
+           /* Check for intersection.  */
+           l = max (loc->address, addr);
+           h = min (loc->address + loc->length, addr + len);
+           if (l < h)
+             return 1;
+         }
+    }
+  return 0;
+}
+
 /* breakpoint_thread_match (PC, PTID) returns true if the breakpoint at
    PC is valid for process/thread PTID.  */
 
index 6587407b8ecb4031176f9b9acfd7d73d7ccf4a78..5ebd36c60db5d4f957da2d1bbeee7d06651035d2 100644 (file)
@@ -736,6 +736,12 @@ extern int regular_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR
 
 extern int software_breakpoint_inserted_here_p (struct address_space *, CORE_ADDR);
 
+/* Returns true if there's a hardware watchpoint or access watchpoint
+   inserted in the range defined by ADDR and LEN.  */
+extern int hardware_watchpoint_inserted_in_range (struct address_space *,
+                                                 CORE_ADDR addr,
+                                                 ULONGEST len);
+
 extern int breakpoint_thread_match (struct address_space *, CORE_ADDR, ptid_t);
 
 extern void until_break_command (char *, int, int);
index 12b74a59fd938e4b92af5afa80cfa760eccb3bf0..d2c6b77d2a984ca6bd302e5fe2f54dcd25d065ba 100644 (file)
@@ -224,6 +224,9 @@ static int (*record_beneath_to_insert_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
 static int (*record_beneath_to_remove_breakpoint) (struct gdbarch *,
                                                   struct bp_target_info *);
+static int (*record_beneath_to_stopped_by_watchpoint) (void);
+static int (*record_beneath_to_stopped_data_address) (struct target_ops *,
+                                                     CORE_ADDR *);
 
 /* Alloc and free functions for record_reg, record_mem, and record_end 
    entries.  */
@@ -673,6 +676,9 @@ record_gdb_operation_disable_set (void)
   return old_cleanups;
 }
 
+/* Flag set to TRUE for target_stopped_by_watchpoint.  */
+static int record_hw_watchpoint = 0;
+
 /* Execute one instruction from the record log.  Each instruction in
    the log will be represented by an arbitrary sequence of register
    entries and memory entries, followed by an 'end' entry.  */
@@ -739,7 +745,22 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
                                entry->u.mem.len);
                   }
                 else
-                  memcpy (record_get_loc (entry), mem, entry->u.mem.len);
+                 {
+                   memcpy (record_get_loc (entry), mem, entry->u.mem.len);
+
+                   /* We've changed memory --- check if a hardware
+                      watchpoint should trap.  Note that this
+                      presently assumes the target beneath supports
+                      continuable watchpoints.  On non-continuable
+                      watchpoints target, we'll want to check this
+                      _before_ actually doing the memory change, and
+                      not doing the change at all if the watchpoint
+                      traps.  */
+                   if (hardware_watchpoint_inserted_in_range
+                       (get_regcache_aspace (regcache),
+                        entry->u.mem.addr, entry->u.mem.len))
+                     record_hw_watchpoint = 1;
+                 }
               }
           }
       }
@@ -770,6 +791,8 @@ static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
 static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
                                        struct bp_target_info *);
+static int (*tmp_to_stopped_by_watchpoint) (void);
+static int (*tmp_to_stopped_data_address) (struct target_ops *, CORE_ADDR *);
 
 static void record_restore (void);
 
@@ -894,6 +917,10 @@ record_open (char *name, int from_tty)
        tmp_to_insert_breakpoint = t->to_insert_breakpoint;
       if (!tmp_to_remove_breakpoint)
        tmp_to_remove_breakpoint = t->to_remove_breakpoint;
+      if (!tmp_to_stopped_by_watchpoint)
+       tmp_to_stopped_by_watchpoint = t->to_stopped_by_watchpoint;
+      if (!tmp_to_stopped_data_address)
+       tmp_to_stopped_data_address = t->to_stopped_data_address;
     }
   if (!tmp_to_xfer_partial)
     error (_("Could not find 'to_xfer_partial' method on the target stack."));
@@ -915,6 +942,8 @@ record_open (char *name, int from_tty)
   record_beneath_to_xfer_partial = tmp_to_xfer_partial;
   record_beneath_to_insert_breakpoint = tmp_to_insert_breakpoint;
   record_beneath_to_remove_breakpoint = tmp_to_remove_breakpoint;
+  record_beneath_to_stopped_by_watchpoint = tmp_to_stopped_by_watchpoint;
+  record_beneath_to_stopped_data_address = tmp_to_stopped_data_address;
 
   if (current_target.to_stratum == core_stratum)
     record_core_open_1 (name, from_tty);
@@ -1069,14 +1098,23 @@ record_wait (struct target_ops *ops,
                {
                  struct regcache *regcache;
 
-                 /* Yes -- check if there is a breakpoint.  */
+                 /* Yes -- this is likely our single-step finishing,
+                    but check if there's any reason the core would be
+                    interested in the event.  */
+
                  registers_changed ();
                  regcache = get_current_regcache ();
                  tmp_pc = regcache_read_pc (regcache);
-                 if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
-                                                 tmp_pc))
+
+                 if (target_stopped_by_watchpoint ())
+                   {
+                     /* Always interested in watchpoints.  */
+                   }
+                 else if (breakpoint_inserted_here_p (get_regcache_aspace (regcache),
+                                                      tmp_pc))
                    {
-                     /* There is a breakpoint.  GDB will want to stop.  */
+                     /* There is a breakpoint here.  Let the core
+                        handle it.  */
                      struct gdbarch *gdbarch = get_regcache_arch (regcache);
                      CORE_ADDR decr_pc_after_break
                        = gdbarch_decr_pc_after_break (gdbarch);
@@ -1086,10 +1124,8 @@ record_wait (struct target_ops *ops,
                    }
                  else
                    {
-                     /* There is not a breakpoint, and gdb is not
-                        stepping, therefore gdb will not stop.
-                        Therefore we will not return to gdb.
-                        Record the insn and resume.  */
+                     /* This must be a single-step trap.  Record the
+                        insn and issue another step.  */
                      if (!do_record_message (regcache, TARGET_SIGNAL_0))
                        break;
 
@@ -1116,6 +1152,7 @@ record_wait (struct target_ops *ops,
       struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0);
       CORE_ADDR tmp_pc;
 
+      record_hw_watchpoint = 0;
       status->kind = TARGET_WAITKIND_STOPPED;
 
       /* Check breakpoint when forward execute.  */
@@ -1219,6 +1256,14 @@ record_wait (struct target_ops *ops,
                                           gdbarch_decr_pc_after_break (gdbarch));
                      continue_flag = 0;
                    }
+
+                 if (record_hw_watchpoint)
+                   {
+                     if (record_debug)
+                       fprintf_unfiltered (gdb_stdlog,
+                                           "Process record: hit hw watchpoint.\n");
+                     continue_flag = 0;
+                   }
                  /* Check target signal */
                  if (record_list->u.end.sigval != TARGET_SIGNAL_0)
                    /* FIXME: better way to check */
@@ -1260,6 +1305,24 @@ replay_out:
   return inferior_ptid;
 }
 
+static int
+record_stopped_by_watchpoint (void)
+{
+  if (RECORD_IS_REPLAY)
+    return record_hw_watchpoint;
+  else
+    return record_beneath_to_stopped_by_watchpoint ();
+}
+
+static int
+record_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
+{
+  if (RECORD_IS_REPLAY)
+    return 0;
+  else
+    return record_beneath_to_stopped_data_address (ops, addr_p);
+}
+
 /* "to_disconnect" method for process record target.  */
 
 static void
@@ -1594,6 +1657,7 @@ init_record_ops (void)
   record_ops.to_xfer_partial = record_xfer_partial;
   record_ops.to_insert_breakpoint = record_insert_breakpoint;
   record_ops.to_remove_breakpoint = record_remove_breakpoint;
+  record_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
   record_ops.to_can_execute_reverse = record_can_execute_reverse;
   record_ops.to_stratum = record_stratum;
   /* Add bookmark target methods.  */
@@ -1801,6 +1865,7 @@ init_record_core_ops (void)
   record_core_ops.to_xfer_partial = record_core_xfer_partial;
   record_core_ops.to_insert_breakpoint = record_core_insert_breakpoint;
   record_core_ops.to_remove_breakpoint = record_core_remove_breakpoint;
+  record_core_ops.to_stopped_by_watchpoint = record_stopped_by_watchpoint;
   record_core_ops.to_can_execute_reverse = record_can_execute_reverse;
   record_core_ops.to_has_execution = record_core_has_execution;
   record_core_ops.to_stratum = record_stratum;