This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Program Breakpoints


Hello GDB reviewers,

This is the result of the RFC discussion on the GDB list late March.
I have simplified and updated the feature as discussed. In addition
I have supported it in gdbserver (only the Xtensa target so far).
In gdb and gdbserver, target support is optional, and targets that
do not support it behave as before. User guide updates are included.

*** A patch is submitted for your review (attached). ***

A detailed discussion follows...

A "program breakpoint" is a trap instruction inherent to the target
program and unknown to GDB. It is like a permanent breakpoint except:
- it is unknown to GDB (not in the breakpoint table).
- it need not be the same instruction used by GDB for breakpoints.

Here is the current behavior this patch fixes:

GDB cannot distinguish a SIGTRAP due to a trap instruction in the
program from one due to single stepping (or other reasons). Breakpoints
are recognized only if they are known to (and usually planted by) GDB.
A trap instruction inherent in the target program is not recognized.
If the target hits one during normal run, GDB receives a SIGTRAP and
sees it as a random signal, reports SIGTRAP and (fortuituously) stops.
If this happens during stepping, GDB assumes the SIGTRAP is from a
single-step and keeps going, repeatedly hitting the trap. A user using
software watchpoints expects slow exection, so may wait a long time
before suspecting a problem, only to find out GDB is hung.

Here's a description of the feature and the reasons for it:

A new target.h macro is provided to allow GDB to determine that it
was stopped by a trap instruction: STOPPED_BY_TRAP_INSTRUCTION(size).
The optional size parameter is a pointer into which the size of the
instruction is written if the PC is pointing to it, allowing GDB to
skip it on resume or step. Targets that implement this recognize
program breakpoints by knowing they hit a trap that is not in the
breakpoint table. A GDB planted breakpoint always takes precedence
over a program breakpoint (in fact GDB might plant a breakpoint
over a program breakpoint). I have only implemented it for the
(remote) targets we support.

Any target may implement this macro, receiving information from the
inferior or probing the instruction in memory, as appropriate. For
remote targets it is usually much more efficient to have the target
communicate that it hit a trap instruction (especially since some
targets may modify the PC to back out of an exception handler the
trap is used in, leaving GDB no way to know directly).

Remote targets may notify GDB of hitting a trap instruction by an
extension to the stop-reply packet for SIGTRAP: "TAAtrap:r", where
r is a single digit representing the size of the trap instruction
if and only if the PC is pointing to it (it needs to be skipped).
That result is passed to infrun and infcmd via the macro above.
Note that the remote target is not expected to distinguish a
program breakpoint from a GDB-planted software breakpoint (that
distinction is made in infrun by looking up the breakpoint table).

When GDB detects a program breakpoint (even while stepping) it
stops and reports it to both CLI and MI. When stopped by one (and
if the PC has not been altered while stopped), on resume or step
GDB will step over it by incrementing the PC by the size (if != 0).
In the case of step-instruction where PC points to the trap, the
first step does not actually run the inferior but merely increments
the PC, simulating the expected advance of 1 instruction.

gdbserver has been modified to recognize a trap as distinct from
a single step, and pass the information from the low target up
to the remote interface. The target must support this for the
information to be passed to GDB via the stop-reply extension.

This has been implemented and well tested in Xtensa toolchains
with the Xtensa instruction set simulator as the target agent.
It has now also been tested with gdbserver on Xtensa. No other
targets yet implement this.

The user manual has been updated. I will update the internals
manual after this patch has been (possibly changed and) accepted.

We have a target specific regression test internally. However I
don't know how to make a generic test case because it needs to
embed an arch-specific trap instruction with an __asm__ construct.
Advice on this would be appreciated. We could submit the test as
a target or arch specific test.

In merging from our GDB 6.8 based internal source base (on which
our extensive testing was done) to the CVS head, I had to merge
with non-stop thread support and reverse debugging. I believe
I have handled those correctly, but have no target to test them
on. I hope someone can integrate this patch and run all the
regressions to make sure I didn't break anything.

Thanks and best regards,
Ross

--
Ross Morley
ross@tensilica.com
ross@computer.org


2009-04-17  Ross Morley  <ross@computer.org>

doc
	* doc/gdb.texinfo: Mention program breakpoints and associated remote 
	protocol 'TAAtrap:r' stop-reply packet extension, and MI async record.

gdb
	* NEWS: GDB recognizes program breakpoints (trap instructions in
	the target program and unknown to GDB) and can step over them.
	Includes remote protocol extension and requires target support.
	* gdbthread.h (struct thread_info): Add program_breakpoint_hit and
	program_breakpoint_size.
	* remote.c (remote_stopped_by_trap_instruction_p): New.
	(remote_trap_instruction_size): New.
	(struct stop_reply): New stopped_by_trap_instruction_p and
	trap_instruction_size.
	(remote_parse_stop_reply): Handle 'trap:size' remote protocol extension.
	(process_stop_reply): Set remote_stopped_by_trap_instruction_p and
	remote_trap_instruction_size from stop_reply.
	(remote_wait_as): Initialize remote_stopped_by_trap_instruction_p.
	(remote_stopped_by_trap_instruction): New.
	(init_remote_ops): Initialize new callback in remote_ops.
	* target.h (struct target_ops):  Add to_stopped_by_trap_instruction.
	(STOPPED_BY_TRAP_INSTRUCTION): New.
	* target.c (update_current_target): Inherit and de_fault 
	to_stopped_by_trap_instruction.
	(debug_to_stopped_by_trap_instruction): New.
	(setup_target_debug): Initialize to_stopped_by_trap_instruction.
	* infcmd.c: Include mi/mi-common.h.
	(step_1): Step-i over program breakpoint and report; consolidate tp.
	* infrun.c (resume): Skip program breakpoint.
	(enum inferior_stop_reason): Add PROGRAM_BREAKPOINT stop reason.
	(handle_inferior_event): Recognize program breakpoint hit and avoid 
	treating it as a random signal. Add debug diagnostic.
	(process_event_stop_test): Handle program breakpoint hit.
	(print_stop_reason): Handle PROGRAM_BREAKPOINT stop reason.
	(normal_stop): Print stop reason and location for program breakpoint.
	* mi/mi_common.h (enum async_reply_reason): New async reply reason
	EXEC_ASYNC_PROGRAM_BREAKPOINT.
	* mi/mi_common.c (async_reason_string_lookup): New program-breakpoint
	reason string for EXEC_ASYNC_PROGRAM_BREAKPOINT.

gdbserver
	* NEWS: gdbserver recognizes trap instructions whether or not they 
	are used by GDB for breakpoints, and reports them to the host GDB.
	* linux-low.c (linux_stopped_by_trap_instruction): New.
	(struct target_ops): Add linux_stopped_by_trap_instruction.
	* linux-low.h (struct linux_target_ops): New trap_size_at function.
	Comment breakpoint_at to clarify distinction from trap_size_at.
	* linux-xtensa-low.c (XTENSA_BREAKPOINT): Comment only 'density'.
	(xtensa_trap_size-at): New.
	(struct linux_target_ops): Add xtensa_trap_size_at + padding.
	Change '0' entries to 'NULL' for pointer fields.
	* remote-utils.c (prepare_resume_reply): Append 'trap:r' extension 
	to 'TAA' stop-reply for SIGTRAP when stopped by trap instruction.
	* target.h (struct target_ops): Add stopped_by_trap_instruction.

Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.580
diff -u -r1.580 gdb.texinfo
--- gdb/doc/gdb.texinfo	15 Apr 2009 22:20:32 -0000	1.580
+++ gdb/doc/gdb.texinfo	17 Apr 2009 18:55:23 -0000
@@ -2961,6 +2961,14 @@
 (for example, routines that are arguments in a @code{pthread_create}
 call).
 
+Some programs may contain embedded break or trap instructions that are
+unknown to GDB. These are called @dfn{program breakpoints} because they 
+belong to the program itself. If encountered, a target may stop execution 
+and report this to GDB along with how much to increment the PC to step 
+over it (a target is not required to do this). Because they are part of
+the program code, program breakpoints cannot be deleted or disabled and
+do not show up in @value{GDBN}'s list of breakpoints.
+
 @cindex watchpoints
 @cindex data breakpoints
 @cindex memory tracing
@@ -20171,6 +20179,8 @@
 The inferior exited normally.
 @item signal-received
 A signal was received by the inferior.
+@item program-breakpoint
+A program breakpoint (trap instruction unknown to GDB) was reached.
 @end table

 The @var{id} field identifies the thread that directly caused the stop
@@ -26723,6 +26731,12 @@
 @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
 list of loaded libraries.  @var{r} is ignored.
 
+@item trap
+The packet indicates that a target-specific break or trap instruction 
+was hit. @var{r} is the size of the instruction if the PC is pointing
+to it, else 0 (for example if the hardware already incremented the PC).
+@var{r} is ignored if the instruction was planted by @value{GDBN}.
+
 The packet indicates that the target cannot continue replaying 
Index: gdb/NEWS
===================================================================
RCS file: /cvs/src/src/gdb/NEWS,v
retrieving revision 1.305
diff -u -r1.305 NEWS
--- gdb/NEWS	31 Mar 2009 20:21:06 -0000	1.305
+++ gdb/NEWS	17 Apr 2009 18:55:25 -0000
@@ -3,6 +3,10 @@
 
 *** Changes since GDB 6.8
 
+* GDB now supports handling (embedded) program break or trap instructions
+that are unknown to GDB. These are called program breakpoints because they
+belong to the program itself.
+
 * GDB now has support for multi-byte and wide character sets on the
 target.  Strings whose character type is wchar_t, char16_t, or
 char32_t are now correctly printed.  GDB supports wide- and unicode-
Index: gdb/gdbthread.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbthread.h,v
retrieving revision 1.47
diff -u -r1.47 gdbthread.h
--- gdb/gdbthread.h	31 Mar 2009 15:23:57 -0000	1.47
+++ gdb/gdbthread.h	17 Apr 2009 18:55:26 -0000
@@ -131,6 +131,13 @@
      back to user code before stopping and reporting the event.  */
   int stepping_through_solib_after_catch;
 
+  /* program_breakpoint_hit is set when a break or trap instruction was hit at 
+     an address not in the breakpoint table (it's part of the program). If so,
+     program_breakpoint_size is the size of the instruction if it needs to be 
+     skipped, else 0 (for example, the target already incremented the PC).  */
+  int program_breakpoint_hit;
+  int program_breakpoint_size;
+
   /* When stepping_through_solib_after_catch is TRUE, this is a
      list of the catchpoints that should be reported as triggering
      when we finally do stop stepping.  */
Index: gdb/remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.354
diff -u -r1.354 remote.c
--- gdb/remote.c	16 Apr 2009 19:31:03 -0000	1.354
+++ gdb/remote.c	17 Apr 2009 18:55:29 -0000
@@ -528,6 +528,17 @@
 /* This is non-zero if target stopped for a watchpoint.  */
 static int remote_stopped_by_watchpoint_p;
 
+/* This is non-zero if target stopped for a trap instruction, whether or
+   not it was a software breakpoint planted by GDB. */
+static int remote_stopped_by_trap_instruction_p = 0;
+
+/* When remote_stopped_by_trap_instruction_p is non-zero, this is the size 
+   of the instruction (or 0 if it should not be skipped) as reported by the 
+   target.  If non-zero GDB must increment the PC by size before resuming.
+   Generally this is non-zero if and only if the target stopped with PC at 
+   the breakpoint.  */
+static int remote_trap_instruction_size = 0;
+
 static struct target_ops remote_ops;
 
 static struct target_ops extended_remote_ops;
@@ -4157,6 +4168,9 @@
   int stopped_by_watchpoint_p;
   CORE_ADDR watch_data_address;
 
+  int stopped_by_trap_instruction_p;
+  int trap_instruction_size;
+
   int solibs_changed;
   int replay_event;
 };
@@ -4321,6 +4335,7 @@
   event->solibs_changed = 0;
   event->replay_event = 0;
   event->stopped_by_watchpoint_p = 0;
+  event->stopped_by_trap_instruction_p = 0;
   event->regcache = NULL;
 
   switch (buf[0])
@@ -4343,6 +4358,7 @@
 	    char *p_temp;
 	    int fieldsize;
 	    LONGEST pnum = 0;
+	    ULONGEST size;
 
 	    /* If the packet contains a register number, save it in
 	       pnum and set p1 to point to the character following it.
@@ -4387,6 +4403,12 @@
 		    event->solibs_changed = 1;
 		    p = p_temp;
 		  }
+		else if (strncmp (p, "trap", p1 - p) == 0)
+		  {
+		    event->stopped_by_trap_instruction_p = 1;
+		    p = unpack_varlen_hex (++p1, &size);
+		    event->trap_instruction_size = (int) size;
+		  }
 		else if (strncmp (p, "replaylog", p1 - p) == 0)
 		  {
 		    /* NO_HISTORY event.
@@ -4637,6 +4659,10 @@
       remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
       remote_watch_data_address = stop_reply->watch_data_address;
 
+      remote_stopped_by_trap_instruction_p 
+	= stop_reply->stopped_by_trap_instruction_p;
+      remote_trap_instruction_size = stop_reply->trap_instruction_size;
+
       remote_notice_new_inferior (ptid, 0);
     }
 
@@ -4756,6 +4782,7 @@
   buf = rs->buf;
 
   remote_stopped_by_watchpoint_p = 0;
+  remote_stopped_by_trap_instruction_p = 0;
 
   /* We got something.  */
   rs->waiting_for_stop_reply = 0;
@@ -7041,6 +7068,13 @@
   return rc;
 }
 
+static int
+remote_stopped_by_trap_instruction (int *size)
+{
+  if (remote_stopped_by_trap_instruction_p && size)
+    *size = remote_trap_instruction_size;
+  return remote_stopped_by_trap_instruction_p;
+}
 
 static int
 remote_insert_hw_breakpoint (struct bp_target_info *bp_tgt)
@@ -8800,6 +8834,8 @@
   remote_ops.to_remove_breakpoint = remote_remove_breakpoint;
   remote_ops.to_stopped_by_watchpoint = remote_stopped_by_watchpoint;
   remote_ops.to_stopped_data_address = remote_stopped_data_address;
+  remote_ops.to_stopped_by_trap_instruction =
+    				remote_stopped_by_trap_instruction;
   remote_ops.to_can_use_hw_breakpoint = remote_check_watch_resources;
   remote_ops.to_insert_hw_breakpoint = remote_insert_hw_breakpoint;
   remote_ops.to_remove_hw_breakpoint = remote_remove_hw_breakpoint;
Index: gdb/target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.149
diff -u -r1.149 target.h
--- gdb/target.h	17 Mar 2009 19:28:09 -0000	1.149
+++ gdb/target.h	17 Apr 2009 18:55:31 -0000
@@ -372,6 +372,7 @@
     int (*to_watchpoint_addr_within_range) (struct target_ops *,
 					    CORE_ADDR, CORE_ADDR, int);
     int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int);
+    int (*to_stopped_by_trap_instruction) (int *);
     void (*to_terminal_init) (void);
     void (*to_terminal_inferior) (void);
     void (*to_terminal_ours_for_output) (void);
@@ -1127,6 +1128,14 @@
 
 extern const struct target_desc *target_read_description (struct target_ops *);
 
+/* Returns non-zero if we were stopped by a trap or break instruction,
+   whether or not it is a software break planted by GDB. If size != 0,
+   sets *size to that of the instruction or 0 if it need not be skipped.  */
+
+#ifndef STOPPED_BY_TRAP_INSTRUCTION
+#define STOPPED_BY_TRAP_INSTRUCTION(size) \
+  (*current_target.to_stopped_by_trap_instruction) (size)
+#endif
 #define target_get_ada_task_ptid(lwp, tid) \
      (*current_target.to_get_ada_task_ptid) (lwp,tid)
 
Index: gdb/target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.206
diff -u -r1.206 target.c
--- gdb/target.c	14 Apr 2009 16:48:07 -0000	1.206
+++ gdb/target.c	17 Apr 2009 18:55:32 -0000
@@ -115,6 +115,8 @@
 
 static int debug_to_remove_watchpoint (CORE_ADDR, int, int);
 
+static int debug_to_stopped_by_trap_instruction (int *);
+
 static int debug_to_stopped_by_watchpoint (void);
 
 static int debug_to_stopped_data_address (struct target_ops *, CORE_ADDR *);
@@ -435,6 +437,7 @@
       INHERIT (to_insert_watchpoint, t);
       INHERIT (to_remove_watchpoint, t);
       INHERIT (to_stopped_data_address, t);
+      INHERIT (to_stopped_by_trap_instruction, t);
       INHERIT (to_have_steppable_watchpoint, t);
       INHERIT (to_have_continuable_watchpoint, t);
       INHERIT (to_stopped_by_watchpoint, t);
@@ -551,6 +554,9 @@
   de_fault (to_stopped_data_address,
 	    (int (*) (struct target_ops *, CORE_ADDR *))
 	    return_zero);
+  de_fault (to_stopped_by_trap_instruction,
+            (int (*) (int*))
+            return_zero);
   de_fault (to_watchpoint_addr_within_range,
 	    default_watchpoint_addr_within_range);
   de_fault (to_region_ok_for_hw_watchpoint,
@@ -2994,6 +3000,19 @@
   return retval;
 }
 
+static int
+debug_to_stopped_by_trap_instruction (int *size)
+{
+  int retval;
+
+  retval = debug_target.to_stopped_by_trap_instruction (size);
+
+  fprintf_unfiltered (gdb_stdlog,
+		      "target_stopped_by_trap_instruction (%d) = %d\n",
+		      *size, retval);
+  return retval;
+}
+
 static void
 debug_to_terminal_init (void)
 {
@@ -3230,6 +3249,7 @@
   current_target.to_remove_watchpoint = debug_to_remove_watchpoint;
   current_target.to_stopped_by_watchpoint = debug_to_stopped_by_watchpoint;
   current_target.to_stopped_data_address = debug_to_stopped_data_address;
+  current_target.to_stopped_by_trap_instruction = debug_to_stopped_by_trap_instruction;
   current_target.to_watchpoint_addr_within_range = debug_to_watchpoint_addr_within_range;
   current_target.to_region_ok_for_hw_watchpoint = debug_to_region_ok_for_hw_watchpoint;
   current_target.to_terminal_init = debug_to_terminal_init;
Index: gdb/infcmd.c
===================================================================
RCS file: /cvs/src/src/gdb/infcmd.c,v
retrieving revision 1.238
diff -u -r1.238 infcmd.c
--- gdb/infcmd.c	25 Mar 2009 21:42:34 -0000	1.238
+++ gdb/infcmd.c	17 Apr 2009 18:55:34 -0000
@@ -52,6 +52,7 @@
 #include "cli/cli-decode.h"
 #include "gdbthread.h"
 #include "valprint.h"
+#include "mi/mi-common.h"
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -786,6 +787,7 @@
 {
   int count = 1;
   struct frame_info *frame;
+  struct thread_info *tp = inferior_thread ();
   struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
   int async_exec = 0;
   int thread = -1;
@@ -821,13 +823,40 @@
 
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
+  if (tp->program_breakpoint_hit && tp->program_breakpoint_size != 0
+      && execution_direction != EXEC_REVERSE && read_pc () == stop_pc
+      && count > 0)
+    {
+      /* "stepi" off program breakpoint: the first step is to just increment
+	 the PC past the break, then there are count-1 steps to go.
+	 Note proceed() won't be called the first time, and on subsequent
+	 steps the PC will already be off the break, so the entire handling
+	 of "stepi" off a program breakpoint is done here. If stopping after 
+	 the break, display location information as for normal_stop.  */
+      count--;
+      write_pc (read_pc () + tp->program_breakpoint_size);
+      if (count == 0)
+	{
+	  reinit_frame_cache ();
+	  if (ui_out_is_mi_like_p (uiout))
+	    {
+	      ui_out_field_string 
+		(uiout, "reason", 
+		 async_reason_lookup (EXEC_ASYNC_END_STEPPING_RANGE));
+	      ui_out_field_int (uiout, "thread-id",
+				pid_to_thread_id (inferior_ptid));
+	      print_stack_frame (get_selected_frame (NULL), -1, LOC_AND_ADDRESS);
+	    }
+	  else 
+	    print_stack_frame (get_selected_frame (NULL), -1, SRC_LINE);
+	}
+    }
 
   /* In synchronous case, all is well; each step_once call will step once.  */
   if (!target_can_async_p ())
     {
       for (; count > 0; count--)
 	{
-	  struct thread_info *tp;
 	  step_once (skip_subroutines, single_inst, count, thread);
 
 	  if (target_has_execution
Index: gdb/infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.368
diff -u -r1.368 infrun.c
--- gdb/infrun.c	14 Apr 2009 00:59:47 -0000	1.368
+++ gdb/infrun.c	17 Apr 2009 18:55:36 -0000
@@ -1029,6 +1029,11 @@
 a command like `return' or `jump' to continue execution."));
     }
 
+  /* Skip a program breakpoint (unless PC changed without running inferior).  */
+  if (tp->program_breakpoint_hit && tp->program_breakpoint_size != 0
+      && execution_direction != EXEC_REVERSE && read_pc () == stop_pc)
+    write_pc (read_pc () + tp->program_breakpoint_size);
+
   /* If enabled, step over breakpoints by executing a copy of the
      instruction at a different address.
 
@@ -1564,7 +1569,9 @@
   /* Inferior received signal, and user asked to be notified. */
   SIGNAL_RECEIVED,
   /* Reverse execution -- target ran out of history info.  */
-  NO_HISTORY
+  NO_HISTORY,
+  /* Inferior stopped because of program breakpoint.  */
+  PROGRAM_BREAKPOINT
 };
 
 /* The PTID we'll do a target_wait on.*/
@@ -2937,6 +2944,13 @@
       || stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_NO_SIGSTOP
       || stop_soon == STOP_QUIETLY_REMOTE)
     {
+      /* If we hit a trap not inserted by GDB, it must be in the program. */
+      ecs->event_thread->program_breakpoint_hit
+	= (STOPPED_BY_TRAP_INSTRUCTION (&ecs->event_thread->program_breakpoint_size)
+	    && !software_breakpoint_inserted_here_p (stop_pc));
+      if (debug_infrun && ecs->event_thread->program_breakpoint_hit)
+	fprintf_unfiltered (gdb_stdlog, "infrun: program breakpoint hit\n");
+
       if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP && stop_after_trap)
 	{
           if (debug_infrun)
@@ -3016,6 +3030,7 @@
       if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
 	ecs->random_signal
 	  = !(bpstat_explains_signal (ecs->event_thread->stop_bpstat)
+	      || ecs->event_thread->program_breakpoint_hit
 	      || ecs->event_thread->trap_expected
 	      || (ecs->event_thread->step_range_end
 		  && ecs->event_thread->step_resume_breakpoint == NULL));
@@ -3136,6 +3151,21 @@
       return;
     }
 
+  /* Handle case of hitting a program breakpoint (break instruction
+     in target program, not planted by or known to GDB).  */
+  
+  if (ecs->event_thread->program_breakpoint_hit)
+    {
+      stop_print_frame = 1;
+      
+      /* We are about to nuke the step_resume_breakpoint and
+	 through_sigtramp_breakpoint via the cleanup chain, so
+	 no need to worry about it here.  */
+      
+      stop_stepping (ecs);
+      return;
+    }
+
   /* Handle cases caused by hitting a breakpoint.  */
   {
     CORE_ADDR jmp_buf_pc;
@@ -4232,6 +4262,17 @@
       /* Reverse execution: target ran out of history info.  */
       ui_out_text (uiout, "\nNo more reverse-execution history.\n");
       break;
+    case PROGRAM_BREAKPOINT:
+      /* The inferior was stopped by a break/trap inherent in the program. */
+      {
+	if (ui_out_is_mi_like_p (uiout))
+	  ui_out_field_string 
+	    (uiout, "reason", 
+	     async_reason_lookup (EXEC_ASYNC_PROGRAM_BREAKPOINT));
+	ui_out_text (uiout, "\nProgram breakpoint, ");
+	/* to be followed by source location, as for planted breakpoint */
+      }
+      break;
     default:
       internal_error (__FILE__, __LINE__,
 		      _("print_stop_reason: unrecognized enum value"));
@@ -4370,45 +4411,54 @@
 	  int do_frame_printing = 1;
 	  struct thread_info *tp = inferior_thread ();
 
-	  bpstat_ret = bpstat_print (tp->stop_bpstat);
-	  switch (bpstat_ret)
+	  if (tp->program_breakpoint_hit)
+	    {
+	      /* Print program break stop reason and description. */
+	      print_stop_reason (PROGRAM_BREAKPOINT, 0);
+	      source_flag = SRC_AND_LOC;    /* print location and source line */
+	    }
+	  else
 	    {
-	    case PRINT_UNKNOWN:
-	      /* If we had hit a shared library event breakpoint,
-		 bpstat_print would print out this message.  If we hit
-		 an OS-level shared library event, do the same
-		 thing.  */
-	      if (last.kind == TARGET_WAITKIND_LOADED)
+	      bpstat_ret = bpstat_print (tp->stop_bpstat);
+	      switch (bpstat_ret)
 		{
-		  printf_filtered (_("Stopped due to shared library event\n"));
+		case PRINT_UNKNOWN:
+		  /* If we had hit a shared library event breakpoint,
+		     bpstat_print would print out this message.  If we hit
+		     an OS-level shared library event, do the same
+		     thing.  */
+		  if (last.kind == TARGET_WAITKIND_LOADED)
+		    {
+		      printf_filtered (_("Stopped due to shared library event\n"));
+		      source_flag = SRC_LINE;	/* something bogus */
+		      do_frame_printing = 0;
+		      break;
+		    }
+
+		  /* FIXME: cagney/2002-12-01: Given that a frame ID does
+		     (or should) carry around the function and does (or
+		     should) use that when doing a frame comparison.  */
+		  if (tp->stop_step
+		      && frame_id_eq (tp->step_frame_id,
+				      get_frame_id (get_current_frame ()))
+		      && step_start_function == find_pc_function (stop_pc))
+		    source_flag = SRC_LINE;	/* finished step, print source line */
+		  else
+		    source_flag = SRC_AND_LOC;	/* print location and source line */
+		  break;
+		case PRINT_SRC_AND_LOC:
+		  source_flag = SRC_AND_LOC;	/* print location and source line */
+		  break;
+		case PRINT_SRC_ONLY:
+		  source_flag = SRC_LINE;
+		  break;
+		case PRINT_NOTHING:
 		  source_flag = SRC_LINE;	/* something bogus */
 		  do_frame_printing = 0;
 		  break;
+		default:
+		  internal_error (__FILE__, __LINE__, _("Unknown value."));
 		}
-
-	      /* FIXME: cagney/2002-12-01: Given that a frame ID does
-	         (or should) carry around the function and does (or
-	         should) use that when doing a frame comparison.  */
-	      if (tp->stop_step
-		  && frame_id_eq (tp->step_frame_id,
-				  get_frame_id (get_current_frame ()))
-		  && step_start_function == find_pc_function (stop_pc))
-		source_flag = SRC_LINE;	/* finished step, just print source line */
-	      else
-		source_flag = SRC_AND_LOC;	/* print location and source line */
-	      break;
-	    case PRINT_SRC_AND_LOC:
-	      source_flag = SRC_AND_LOC;	/* print location and source line */
-	      break;
-	    case PRINT_SRC_ONLY:
-	      source_flag = SRC_LINE;
-	      break;
-	    case PRINT_NOTHING:
-	      source_flag = SRC_LINE;	/* something bogus */
-	      do_frame_printing = 0;
-	      break;
-	    default:
-	      internal_error (__FILE__, __LINE__, _("Unknown value."));
 	    }
 
 	  /* The behavior of this routine with respect to the source
Index: gdb/mi/mi-common.h
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-common.h,v
retrieving revision 1.7
diff -u -r1.7 mi-common.h
--- gdb/mi/mi-common.h	3 Jan 2009 05:57:57 -0000	1.7
+++ gdb/mi/mi-common.h	17 Apr 2009 18:55:37 -0000
@@ -35,6 +35,7 @@
   EXEC_ASYNC_EXITED,
   EXEC_ASYNC_EXITED_NORMALLY,
   EXEC_ASYNC_SIGNAL_RECEIVED,
+  EXEC_ASYNC_PROGRAM_BREAKPOINT,
   /* This is here only to represent the number of enums.  */
   EXEC_ASYNC_LAST
 };
Index: gdb/mi/mi-common.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-common.c,v
retrieving revision 1.7
diff -u -r1.7 mi-common.c
--- gdb/mi/mi-common.c	21 Feb 2009 16:14:50 -0000	1.7
+++ gdb/mi/mi-common.c	17 Apr 2009 18:55:38 -0000
@@ -33,6 +33,7 @@
   "exited",
   "exited-normally",
   "signal-received",
+  "program-breakpoint",
   NULL
 };
 
Index: gdb/gdbserver/linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.97
diff -u -r1.97 linux-low.c
--- gdb/gdbserver/linux-low.c	3 Apr 2009 11:40:02 -0000	1.97
+++ gdb/gdbserver/linux-low.c	17 Apr 2009 18:55:39 -0000
@@ -2886,6 +2886,29 @@
   return 0;
 }
 
+static int
+linux_stopped_by_trap_instruction (int *size)
+{
+  CORE_ADDR stop_pc = get_stop_pc();
+  int trap_size = 0;
+
+  if (the_low_target.trap_size_at != NULL)
+    trap_size = (*the_low_target.trap_size_at) (stop_pc);
+  else if (the_low_target.breakpoint_at != NULL
+	   && (*the_low_target.breakpoint_at) (stop_pc))
+    trap_size = the_low_target.breakpoint_len;
+
+  if (trap_size != 0)
+    {
+      *size = (the_low_target.get_pc != NULL 
+	       && (*the_low_target.get_pc) () == stop_pc)
+	      ? trap_size : 0;
+      return 1;
+    }
+  else
+    return 0;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -2923,6 +2946,7 @@
   linux_supports_non_stop,
   linux_async,
   linux_start_non_stop,
+  linux_stopped_by_trap_instruction,
 };
 
 static void
Index: gdb/gdbserver/linux-low.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v
retrieving revision 1.28
diff -u -r1.28 linux-low.h
--- gdb/gdbserver/linux-low.h	1 Apr 2009 22:50:24 -0000	1.28
+++ gdb/gdbserver/linux-low.h	17 Apr 2009 18:55:40 -0000
@@ -77,6 +77,9 @@
 
 
   int decr_pc_after_break;
+
+  /* Returns non-zero if there is a breakpoint at the PC (specifically, if 
+     the target memory at PC matches the breakpoint field of this struct).  */
   int (*breakpoint_at) (CORE_ADDR pc);
 
   /* Watchpoint related functions.  See target.h for comments.  */
@@ -89,6 +92,11 @@
      for registers smaller than an xfer unit).  */
   void (*collect_ptrace_register) (int regno, char *buf);
   void (*supply_ptrace_register) (int regno, const char *buf);
+
+  /* Returns non-zero if there is any kind of trap instruction at the PC,
+     including but not limited to the instruction used for GDB breakpoints.
+     If so, the result is the size of the trap (PC increment to skip).  */
+  int (*trap_size_at) (CORE_ADDR pc);
 };
 
 extern struct linux_target_ops the_low_target;
Index: gdb/gdbserver/linux-xtensa-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-xtensa-low.c,v
retrieving revision 1.4
diff -u -r1.4 linux-xtensa-low.c
--- gdb/gdbserver/linux-xtensa-low.c	22 Mar 2009 23:57:10 -0000	1.4
+++ gdb/gdbserver/linux-xtensa-low.c	17 Apr 2009 18:55:41 -0000
@@ -140,6 +140,7 @@
   { 0, 0, -1, -1, NULL, NULL }
 };
 
+/* Currently assumes the Xtensa 'density' option is configured: 'break.n 0'. */
 #if XCHAL_HAVE_BE
 #define XTENSA_BREAKPOINT {0xd2,0x0f}
 #else
@@ -175,12 +176,37 @@
     return memcmp((char *)&insn, xtensa_breakpoint, xtensa_breakpoint_len) == 0;
 }
 
+int
+xtensa_trap_size_at (CORE_ADDR pc)
+{
+    unsigned char insn[3];
+
+    (*the_target->read_memory) (pc, insn, 3);
+
+#if XCHAL_HAVE_BE
+    if (insn[1] == 0xd2 && (insn[2] & 0x0f) == 0x0f)
+#else
+    if (insn[0] == 0x2d && (insn[1] & 0xf0) == 0xf0)
+#endif
+      /* break.n s (density) */
+      return 2;
+#if XCHAL_HAVE_BE
+    else if ((insn[2] & 0xf0) == 0x00 && (insn[1] & 0x0f) == 0x04 && insn[0] == 0x00)
+#else
+    else if ((insn[0] & 0x0f) == 0x00 && (insn[1] & 0xf0) == 0x40 && insn[2] == 0x00)
+#endif
+      /* break s, t */
+      return 3;
+    else 
+      return 0;
+}
+
 struct linux_target_ops the_low_target = {
   init_registers_xtensa,
   0,
-  0,
-  0,
-  0,
+  NULL,
+  NULL,
+  NULL,
   xtensa_get_pc,
   xtensa_set_pc,
   xtensa_breakpoint,
@@ -188,4 +214,11 @@
   NULL,
   0,
   xtensa_breakpoint_at,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  xtensa_trap_size_at,
 };
Index: gdb/gdbserver/remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.66
diff -u -r1.66 remote-utils.c
--- gdb/gdbserver/remote-utils.c	3 Apr 2009 14:38:38 -0000	1.66
+++ gdb/gdbserver/remote-utils.c	17 Apr 2009 18:55:42 -0000
@@ -1083,6 +1083,7 @@
       {
 	struct thread_info *saved_inferior;
 	const char **regp;
+	int size;
 
 	sprintf (buf, "T%02x", status->value.sig);
 	buf += strlen (buf);
@@ -1113,6 +1114,14 @@
 	    *buf++ = ';';
 	  }
 
+	else if (the_target->stopped_by_trap_instruction != NULL
+		 && (*the_target->stopped_by_trap_instruction) (&size))
+	  {
+	    sprintf (buf, "trap:%1x", size);
+	    buf += 6;
+	    *buf++ = ';';
+	  }
+
 	while (*regp)
 	  {
 	    buf = outreg (find_regno (*regp), buf);
Index: gdb/gdbserver/target.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/target.h,v
retrieving revision 1.36
diff -u -r1.36 target.h
--- gdb/gdbserver/target.h	1 Apr 2009 22:50:24 -0000	1.36
+++ gdb/gdbserver/target.h	17 Apr 2009 18:55:43 -0000
@@ -275,6 +275,14 @@
   /* Switch to non-stop (1) or all-stop (0) mode.  Return 0 on
      success, -1 otherwise.  */
   int (*start_non_stop) (int);
+
+  /* Returns non-zero if target was stopped by any trap instruction, else 0.
+     It may or may not have been a breakpoint planted by gdbserver or GDB.
+     If stopped by a trap and size != NULL, then *size is set to the size of
+     the trap instruction if the PC still points to it (for skipping), else 0.
+     This information is passed via the remote protocol to GDB.  */
+
+  int (*stopped_by_trap_instruction) (int *size);
 };
 
 extern struct target_ops *the_target;

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]