record-full/record-btrace: software/hardware breakpoint trap
authorPedro Alves <palves@redhat.com>
Wed, 4 Mar 2015 20:41:15 +0000 (20:41 +0000)
committerPedro Alves <palves@redhat.com>
Wed, 4 Mar 2015 20:41:15 +0000 (20:41 +0000)
This adjusts the record targets to tell the core whether a trap was
caused by a breakpoint.  Targets that can do this should report
breakpoint traps with the PC already adjusted, so this removes the
re-incrementing record-full was doing.

These targets need to be adjusted before process_stratum targets
beneath are, otherwise target_supports_stopped_by_sw_breakpoint,
etc. would fall through to the target beneath while
recording/replaying, and the core would get confused.

Tested on x86-64 Fedora 20, native and gdbserver.

gdb/ChangeLog:
2015-03-04  Pedro Alves  <palves@redhat.com>

* btrace.h: Include target/waitstatus.h.
(struct btrace_thread_info) <stop_reason>: New field.
* record-btrace.c (record_btrace_step_thread): Use
record_check_stopped_by_breakpoint instead of breakpoint_here_p.
(record_btrace_decr_pc_after_break): Delete.
(record_btrace_stopped_by_sw_breakpoint)
(record_btrace_supports_stopped_by_sw_breakpoint)
(record_btrace_stopped_by_hw_breakpoint)
(record_btrace_supports_stopped_by_hw_breakpoint): New functions.
(init_record_btrace_ops): Install them.
* record-full.c (record_full_hw_watchpoint): Delete and replace
with ...
(record_full_stop_reason): ... this throughout.
(record_full_exec_insn): Adjust.
(record_full_wait_1): Adjust.  No longer re-increment the PC.
(record_full_wait_1): Adjust.  Use
record_check_stopped_by_breakpoint instead of breakpoint_here_p.
(record_full_stopped_by_watchpoint): Adjust.
(record_full_stopped_by_sw_breakpoint)
(record_full_supports_stopped_by_sw_breakpoint)
(record_full_supports_stopped_by_sw_breakpoint)
(record_full_stopped_by_hw_breakpoint)
(record_full_supports_stopped_by_hw_breakpoint): New functions.
(init_record_full_ops, init_record_full_core_ops): Install them.
* record.c (record_check_stopped_by_breakpoint): New function.
* record.h: Include target/waitstatus.h.
(record_check_stopped_by_breakpoint): New declaration.

gdb/ChangeLog
gdb/btrace.h
gdb/record-btrace.c
gdb/record-full.c
gdb/record.c
gdb/record.h

index a1eab85414793a462121f324f890a414c712c322..11f6caa0b64740bc34d20f3ed556d91f8174c1c5 100644 (file)
@@ -1,3 +1,33 @@
+2015-03-04  Pedro Alves  <palves@redhat.com>
+
+       * btrace.h: Include target/waitstatus.h.
+       (struct btrace_thread_info) <stop_reason>: New field.
+       * record-btrace.c (record_btrace_step_thread): Use
+       record_check_stopped_by_breakpoint instead of breakpoint_here_p.
+       (record_btrace_decr_pc_after_break): Delete.
+       (record_btrace_stopped_by_sw_breakpoint)
+       (record_btrace_supports_stopped_by_sw_breakpoint)
+       (record_btrace_stopped_by_hw_breakpoint)
+       (record_btrace_supports_stopped_by_hw_breakpoint): New functions.
+       (init_record_btrace_ops): Install them.
+       * record-full.c (record_full_hw_watchpoint): Delete and replace
+       with ...
+       (record_full_stop_reason): ... this throughout.
+       (record_full_exec_insn): Adjust.
+       (record_full_wait_1): Adjust.  No longer re-increment the PC.
+       (record_full_wait_1): Adjust.  Use
+       record_check_stopped_by_breakpoint instead of breakpoint_here_p.
+       (record_full_stopped_by_watchpoint): Adjust.
+       (record_full_stopped_by_sw_breakpoint)
+       (record_full_supports_stopped_by_sw_breakpoint)
+       (record_full_supports_stopped_by_sw_breakpoint)
+       (record_full_stopped_by_hw_breakpoint)
+       (record_full_supports_stopped_by_hw_breakpoint): New functions.
+       (init_record_full_ops, init_record_full_core_ops): Install them.
+       * record.c (record_check_stopped_by_breakpoint): New function.
+       * record.h: Include target/waitstatus.h.
+       (record_check_stopped_by_breakpoint): New declaration.
+
 2015-03-04  Pedro Alves  <palves@redhat.com>
 
        enum lwp_stop_reason -> enum target_stop_reason
index 36bf5001f2cdf66d54983eaddac9b7611a530bc2..b7437f173df229c9b87e2302f5d61ce2d6506dbe 100644 (file)
@@ -27,6 +27,7 @@
    list of sequential control-flow blocks, one such list per thread.  */
 
 #include "btrace-common.h"
+#include "target/waitstatus.h" /* For enum target_stop_reason.  */
 
 struct thread_info;
 struct btrace_function;
@@ -258,6 +259,9 @@ struct btrace_thread_info
      Gaps are skipped during replay, so REPLAY always points to a valid
      instruction.  */
   struct btrace_insn_iterator *replay;
+
+  /* Why the thread stopped, if we need to track it.  */
+  enum target_stop_reason stop_reason;
 };
 
 /* Enable branch tracing for a thread.  */
index af7b65feda253181d79634c72edc88b5ff3cd4aa..f2d35a3642ed034d0233326200e003156da850e2 100644 (file)
@@ -1954,7 +1954,8 @@ record_btrace_step_thread (struct thread_info *tp)
                 target_pid_to_str (tp->ptid),
                 core_addr_to_string_nz (insn->pc));
 
-         if (breakpoint_here_p (aspace, insn->pc))
+         if (record_check_stopped_by_breakpoint (aspace, insn->pc,
+                                                 &btinfo->stop_reason))
            return btrace_step_stopped ();
        }
 
@@ -1986,7 +1987,8 @@ record_btrace_step_thread (struct thread_info *tp)
                 target_pid_to_str (tp->ptid),
                 core_addr_to_string_nz (insn->pc));
 
-         if (breakpoint_here_p (aspace, insn->pc))
+         if (record_check_stopped_by_breakpoint (aspace, insn->pc,
+                                                 &btinfo->stop_reason))
            return btrace_step_stopped ();
        }
     }
@@ -2044,18 +2046,58 @@ record_btrace_can_execute_reverse (struct target_ops *self)
   return 1;
 }
 
-/* The to_decr_pc_after_break method of target record-btrace.  */
+/* The to_stopped_by_sw_breakpoint method of target record-btrace.  */
 
-static CORE_ADDR
-record_btrace_decr_pc_after_break (struct target_ops *ops,
-                                  struct gdbarch *gdbarch)
+static int
+record_btrace_stopped_by_sw_breakpoint (struct target_ops *ops)
 {
-  /* When replaying, we do not actually execute the breakpoint instruction
-     so there is no need to adjust the PC after hitting a breakpoint.  */
   if (record_btrace_is_replaying (ops))
-    return 0;
+    {
+      struct thread_info *tp = inferior_thread ();
+
+      return tp->btrace.stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT;
+    }
+
+  return ops->beneath->to_stopped_by_sw_breakpoint (ops->beneath);
+}
+
+/* The to_supports_stopped_by_sw_breakpoint method of target
+   record-btrace.  */
+
+static int
+record_btrace_supports_stopped_by_sw_breakpoint (struct target_ops *ops)
+{
+  if (record_btrace_is_replaying (ops))
+    return 1;
+
+  return ops->beneath->to_supports_stopped_by_sw_breakpoint (ops->beneath);
+}
+
+/* The to_stopped_by_sw_breakpoint method of target record-btrace.  */
+
+static int
+record_btrace_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  if (record_btrace_is_replaying (ops))
+    {
+      struct thread_info *tp = inferior_thread ();
+
+      return tp->btrace.stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT;
+    }
+
+  return ops->beneath->to_stopped_by_hw_breakpoint (ops->beneath);
+}
+
+/* The to_supports_stopped_by_hw_breakpoint method of target
+   record-btrace.  */
+
+static int
+record_btrace_supports_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  if (record_btrace_is_replaying (ops))
+    return 1;
 
-  return ops->beneath->to_decr_pc_after_break (ops->beneath, gdbarch);
+  return ops->beneath->to_supports_stopped_by_hw_breakpoint (ops->beneath);
 }
 
 /* The to_update_thread_list method of target record-btrace.  */
@@ -2238,7 +2280,12 @@ init_record_btrace_ops (void)
   ops->to_goto_record_end = record_btrace_goto_end;
   ops->to_goto_record = record_btrace_goto;
   ops->to_can_execute_reverse = record_btrace_can_execute_reverse;
-  ops->to_decr_pc_after_break = record_btrace_decr_pc_after_break;
+  ops->to_stopped_by_sw_breakpoint = record_btrace_stopped_by_sw_breakpoint;
+  ops->to_supports_stopped_by_sw_breakpoint
+    = record_btrace_supports_stopped_by_sw_breakpoint;
+  ops->to_stopped_by_hw_breakpoint = record_btrace_stopped_by_hw_breakpoint;
+  ops->to_supports_stopped_by_hw_breakpoint
+    = record_btrace_supports_stopped_by_hw_breakpoint;
   ops->to_execution_direction = record_btrace_execution_direction;
   ops->to_prepare_to_generate_core = record_btrace_prepare_to_generate_core;
   ops->to_done_generating_core = record_btrace_done_generating_core;
index c660743a40742cb23f86df9a5d7a716087f2c5f9..0fbb2646e959c68fdc36bd76ed2b72724f5820d0 100644 (file)
@@ -688,7 +688,8 @@ record_full_gdb_operation_disable_set (void)
 }
 
 /* Flag set to TRUE for target_stopped_by_watchpoint.  */
-static int record_full_hw_watchpoint = 0;
+static enum target_stop_reason record_full_stop_reason
+  = TARGET_STOPPED_BY_NO_REASON;
 
 /* Execute one instruction from the record log.  Each instruction in
    the log will be represented by an arbitrary sequence of register
@@ -766,7 +767,7 @@ record_full_exec_insn (struct regcache *regcache,
                    if (hardware_watchpoint_inserted_in_range
                        (get_regcache_aspace (regcache),
                         entry->u.mem.addr, entry->u.mem.len))
-                     record_full_hw_watchpoint = 1;
+                     record_full_stop_reason = TARGET_STOPPED_BY_WATCHPOINT;
                  }
               }
           }
@@ -1079,6 +1080,8 @@ record_full_wait_1 (struct target_ops *ops,
   record_full_get_sig = 0;
   signal (SIGINT, record_full_sig_handler);
 
+  record_full_stop_reason = TARGET_STOPPED_BY_NO_REASON;
+
   if (!RECORD_FULL_IS_REPLAY && ops != &record_full_core_ops)
     {
       if (record_full_resume_step)
@@ -1119,6 +1122,8 @@ record_full_wait_1 (struct target_ops *ops,
                {
                  struct regcache *regcache;
                  struct address_space *aspace;
+                 enum target_stop_reason *stop_reason_p
+                   = &record_full_stop_reason;
 
                  /* Yes -- this is likely our single-step finishing,
                     but check if there's any reason the core would be
@@ -1133,20 +1138,11 @@ record_full_wait_1 (struct target_ops *ops,
                    {
                      /* Always interested in watchpoints.  */
                    }
-                 else if (breakpoint_inserted_here_p (aspace, tmp_pc))
+                 else if (record_check_stopped_by_breakpoint (aspace, tmp_pc,
+                                                              stop_reason_p))
                    {
                      /* There is a breakpoint here.  Let the core
                         handle it.  */
-                     if (software_breakpoint_inserted_here_p (aspace, tmp_pc))
-                       {
-                         struct gdbarch *gdbarch
-                           = get_regcache_arch (regcache);
-                         CORE_ADDR decr_pc_after_break
-                           = target_decr_pc_after_break (gdbarch);
-                         if (decr_pc_after_break)
-                           regcache_write_pc (regcache,
-                                              tmp_pc + decr_pc_after_break);
-                       }
                    }
                  else
                    {
@@ -1205,27 +1201,20 @@ record_full_wait_1 (struct target_ops *ops,
        = make_cleanup (record_full_wait_cleanups, 0);
       CORE_ADDR tmp_pc;
 
-      record_full_hw_watchpoint = 0;
+      record_full_stop_reason = TARGET_STOPPED_BY_NO_REASON;
       status->kind = TARGET_WAITKIND_STOPPED;
 
       /* Check breakpoint when forward execute.  */
       if (execution_direction == EXEC_FORWARD)
        {
          tmp_pc = regcache_read_pc (regcache);
-         if (breakpoint_inserted_here_p (aspace, tmp_pc))
+         if (record_check_stopped_by_breakpoint (aspace, tmp_pc,
+                                                 &record_full_stop_reason))
            {
-             int decr_pc_after_break = target_decr_pc_after_break (gdbarch);
-
              if (record_debug)
                fprintf_unfiltered (gdb_stdlog,
                                    "Process record: break at %s.\n",
                                    paddress (gdbarch, tmp_pc));
-
-             if (decr_pc_after_break
-                 && !record_full_resume_step
-                 && software_breakpoint_inserted_here_p (aspace, tmp_pc))
-               regcache_write_pc (regcache,
-                                  tmp_pc + decr_pc_after_break);
              goto replay_out;
            }
        }
@@ -1293,27 +1282,19 @@ record_full_wait_1 (struct target_ops *ops,
 
                  /* check breakpoint */
                  tmp_pc = regcache_read_pc (regcache);
-                 if (breakpoint_inserted_here_p (aspace, tmp_pc))
+                 if (record_check_stopped_by_breakpoint (aspace, tmp_pc,
+                                                         &record_full_stop_reason))
                    {
-                     int decr_pc_after_break
-                       = target_decr_pc_after_break (gdbarch);
-
                      if (record_debug)
                        fprintf_unfiltered (gdb_stdlog,
                                            "Process record: break "
                                            "at %s.\n",
                                            paddress (gdbarch, tmp_pc));
-                     if (decr_pc_after_break
-                         && execution_direction == EXEC_FORWARD
-                         && !record_full_resume_step
-                         && software_breakpoint_inserted_here_p (aspace,
-                                                                 tmp_pc))
-                       regcache_write_pc (regcache,
-                                          tmp_pc + decr_pc_after_break);
+
                      continue_flag = 0;
                    }
 
-                 if (record_full_hw_watchpoint)
+                 if (record_full_stop_reason == TARGET_STOPPED_BY_WATCHPOINT)
                    {
                      if (record_debug)
                        fprintf_unfiltered (gdb_stdlog,
@@ -1384,7 +1365,7 @@ static int
 record_full_stopped_by_watchpoint (struct target_ops *ops)
 {
   if (RECORD_FULL_IS_REPLAY)
-    return record_full_hw_watchpoint;
+    return record_full_stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
   else
     return ops->beneath->to_stopped_by_watchpoint (ops->beneath);
 }
@@ -1398,6 +1379,40 @@ record_full_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p)
     return ops->beneath->to_stopped_data_address (ops->beneath, addr_p);
 }
 
+/* The to_stopped_by_sw_breakpoint method of target record-full.  */
+
+static int
+record_full_stopped_by_sw_breakpoint (struct target_ops *ops)
+{
+  return record_full_stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT;
+}
+
+/* The to_supports_stopped_by_sw_breakpoint method of target
+   record-full.  */
+
+static int
+record_full_supports_stopped_by_sw_breakpoint (struct target_ops *ops)
+{
+  return 1;
+}
+
+/* The to_stopped_by_hw_breakpoint method of target record-full.  */
+
+static int
+record_full_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  return record_full_stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT;
+}
+
+/* The to_supports_stopped_by_sw_breakpoint method of target
+   record-full.  */
+
+static int
+record_full_supports_stopped_by_hw_breakpoint (struct target_ops *ops)
+{
+  return 1;
+}
+
 /* Record registers change (by user or by GDB) to list as an instruction.  */
 
 static void
@@ -1926,6 +1941,14 @@ init_record_full_ops (void)
   record_full_ops.to_remove_breakpoint = record_full_remove_breakpoint;
   record_full_ops.to_stopped_by_watchpoint = record_full_stopped_by_watchpoint;
   record_full_ops.to_stopped_data_address = record_full_stopped_data_address;
+  record_full_ops.to_stopped_by_sw_breakpoint
+    = record_full_stopped_by_sw_breakpoint;
+  record_full_ops.to_supports_stopped_by_sw_breakpoint
+    = record_full_supports_stopped_by_sw_breakpoint;
+  record_full_ops.to_stopped_by_hw_breakpoint
+    = record_full_stopped_by_hw_breakpoint;
+  record_full_ops.to_supports_stopped_by_hw_breakpoint
+    = record_full_supports_stopped_by_hw_breakpoint;
   record_full_ops.to_can_execute_reverse = record_full_can_execute_reverse;
   record_full_ops.to_stratum = record_stratum;
   /* Add bookmark target methods.  */
@@ -2164,6 +2187,14 @@ init_record_full_core_ops (void)
     = record_full_stopped_by_watchpoint;
   record_full_core_ops.to_stopped_data_address
     = record_full_stopped_data_address;
+  record_full_core_ops.to_stopped_by_sw_breakpoint
+    = record_full_stopped_by_sw_breakpoint;
+  record_full_core_ops.to_supports_stopped_by_sw_breakpoint
+    = record_full_supports_stopped_by_sw_breakpoint;
+  record_full_core_ops.to_stopped_by_hw_breakpoint
+    = record_full_stopped_by_hw_breakpoint;
+  record_full_core_ops.to_supports_stopped_by_hw_breakpoint
+    = record_full_supports_stopped_by_hw_breakpoint;
   record_full_core_ops.to_can_execute_reverse
     = record_full_can_execute_reverse;
   record_full_core_ops.to_has_execution = record_full_core_has_execution;
index 57851ecdfeade666fae7d52931b2eec346852251..a64543aaae3657593b75c7833831e032dd685230 100644 (file)
@@ -189,6 +189,25 @@ record_kill (struct target_ops *t)
   target_kill ();
 }
 
+/* See record.h.  */
+
+int
+record_check_stopped_by_breakpoint (struct address_space *aspace, CORE_ADDR pc,
+                                   enum target_stop_reason *reason)
+{
+  if (breakpoint_inserted_here_p (aspace, pc))
+    {
+      if (hardware_breakpoint_inserted_here_p (aspace, pc))
+       *reason = TARGET_STOPPED_BY_HW_BREAKPOINT;
+      else
+       *reason = TARGET_STOPPED_BY_SW_BREAKPOINT;
+      return 1;
+    }
+
+  *reason = TARGET_STOPPED_BY_NO_REASON;
+  return 0;
+}
+
 /* Implement "show record debug" command.  */
 
 static void
index 771b14d99c0c14a1ccd375b9b8f7e9ee8827cabe..101daae73ae712a90dde3f2af3af8226adc2ee86 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef _RECORD_H_
 #define _RECORD_H_
 
+#include "target/waitstatus.h" /* For enum target_stop_reason.  */
+
 struct cmd_list_element;
 
 extern unsigned int record_debug;
@@ -47,6 +49,17 @@ enum record_print_flag
   RECORD_PRINT_INDENT_CALLS = (1 << 2)
 };
 
+/* Determined whether the target is stopped at a software or hardware
+   breakpoint, based on PC and the breakpoint tables.  The breakpoint
+   type is translated to the appropriate target_stop_reason and
+   written to REASON.  Returns true if stopped at a breakpoint, false
+   otherwise.  */
+
+extern int
+  record_check_stopped_by_breakpoint (struct address_space *aspace,
+                                     CORE_ADDR pc,
+                                     enum target_stop_reason *reason);
+
 /* Wrapper for target_read_memory that prints a debug message if
    reading memory fails.  */
 extern int record_read_memory (struct gdbarch *gdbarch,