2012-02-24 Luis Machado <lgustavo@codesourcery>
authorLuis Machado <luisgpm@br.ibm.com>
Fri, 24 Feb 2012 15:15:56 +0000 (15:15 +0000)
committerLuis Machado <luisgpm@br.ibm.com>
Fri, 24 Feb 2012 15:15:56 +0000 (15:15 +0000)
* server.c (handle_query): Advertise support for target-side
breakpoint condition evaluation.
(process_point_options): New function.
(process_serial_event): When inserting a breakpoint, check for
a target-side condition that should be evaluated.

* mem-break.c: Include regcache.h and ax.h.
(point_cond_list_t): New data structure.
(breakpoint) <cond_list>: New field.
(find_gdb_breakpoint_at): Make non-static.
(delete_gdb_breakpoint_at): Clear any target-side
conditions.
(clear_gdb_breakpoint_conditions): New function.
(add_condition_to_breakpoint): Likewise.
(add_breakpoint_condition): Likewise.
(gdb_condition_true_at_breakpoint): Likewise.
(gdb_breakpoint_here): Return result directly instead
of going through a local variable.

* mem-break.h (find_gdb_breakpoint_at): New prototype.
(clear_gdb_breakpoint_conditions): Likewise.
(add_breakpoint_condition): Likewise.
(gdb_condition_true_at_breakpoint): Likewise.

* linux-low.c (linux_wait_1): Evaluate target-side breakpoint condition.
(need_step_over_p): Take target-side breakpoint condition into
consideration.

gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/mem-break.c
gdb/gdbserver/mem-break.h
gdb/gdbserver/server.c

index c1baa9604b128548adac48bd276b2eb24ed8c8ce..1f28604448d0deb67fd13c86da00d9b2c07b9e4a 100644 (file)
@@ -1,3 +1,33 @@
+2012-02-24  Luis Machado  <lgustavo@codesourcery>
+
+       * server.c (handle_query): Advertise support for target-side
+       breakpoint condition evaluation.
+       (process_point_options): New function.
+       (process_serial_event): When inserting a breakpoint, check for
+       a target-side condition that should be evaluated.
+
+       * mem-break.c: Include regcache.h and ax.h.
+       (point_cond_list_t): New data structure.
+       (breakpoint) <cond_list>: New field.
+       (find_gdb_breakpoint_at): Make non-static.
+       (delete_gdb_breakpoint_at): Clear any target-side
+       conditions.
+       (clear_gdb_breakpoint_conditions): New function.
+       (add_condition_to_breakpoint): Likewise.
+       (add_breakpoint_condition): Likewise.
+       (gdb_condition_true_at_breakpoint): Likewise.
+       (gdb_breakpoint_here): Return result directly instead
+       of going through a local variable.
+
+       * mem-break.h (find_gdb_breakpoint_at): New prototype.
+       (clear_gdb_breakpoint_conditions): Likewise.
+       (add_breakpoint_condition): Likewise.
+       (gdb_condition_true_at_breakpoint): Likewise.
+
+       * linux-low.c (linux_wait_1): Evaluate target-side breakpoint condition.
+       (need_step_over_p): Take target-side breakpoint condition into
+       consideration.
+
 2012-02-24  Luis Machado  <lgustavo@codesourcery>
 
        * server.h: Include tracepoint.h.
index ab34d849131713294027d413e8ce5a84169ab3c7..8a832313756c6b5548cee1620b79a80cc1448507 100644 (file)
@@ -2398,7 +2398,8 @@ Check if we're already there.\n",
                   || event_child->stopped_by_watchpoint
                   || (!step_over_finished
                       && !bp_explains_trap && !trace_event)
-                  || gdb_breakpoint_here (event_child->stop_pc));
+                  || (gdb_breakpoint_here (event_child->stop_pc)
+                  && gdb_condition_true_at_breakpoint (event_child->stop_pc)));
 
   /* We found no reason GDB would want us to stop.  We either hit one
      of our own breakpoints, or finished an internal step GDB
@@ -3261,8 +3262,10 @@ need_step_over_p (struct inferior_list_entry *entry, void *dummy)
   if (breakpoint_here (pc) || fast_tracepoint_jump_here (pc))
     {
       /* Don't step over a breakpoint that GDB expects to hit
-        though.  */
-      if (gdb_breakpoint_here (pc))
+        though.  If the condition is being evaluated on the target's side
+        and it evaluate to false, step over this breakpoint as well.  */
+      if (gdb_breakpoint_here (pc)
+         && gdb_condition_true_at_breakpoint (pc))
        {
          if (debug_threads)
            fprintf (stderr,
index 60b83b103be605d147539f38f8f3ecd7e302b2b3..c9a60358437407c7459eddc77478583b881c96b1 100644 (file)
@@ -20,6 +20,8 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "server.h"
+#include "regcache.h"
+#include "ax.h"
 
 const unsigned char *breakpoint_data;
 int breakpoint_len;
@@ -85,6 +87,16 @@ enum bkpt_type
     other_breakpoint,
   };
 
+struct point_cond_list
+{
+  /* Pointer to the agent expression that is the breakpoint's
+     conditional.  */
+  struct agent_expr *cond;
+
+  /* Pointer to the next condition.  */
+  struct point_cond_list *next;
+};
+
 /* A high level (in gdbserver's perspective) breakpoint.  */
 struct breakpoint
 {
@@ -93,6 +105,12 @@ struct breakpoint
   /* The breakpoint's type.  */
   enum bkpt_type type;
 
+  /* Pointer to the condition list that should be evaluated on
+     the target or NULL if the breakpoint is unconditional or
+     if GDB doesn't want us to evaluate the conditionals on the
+     target's side.  */
+  struct point_cond_list *cond_list;
+
   /* Link to this breakpoint's raw breakpoint.  This is always
      non-NULL.  */
   struct raw_breakpoint *raw;
@@ -632,7 +650,7 @@ delete_breakpoint (struct breakpoint *todel)
   return delete_breakpoint_1 (proc, todel);
 }
 
-static struct breakpoint *
+struct breakpoint *
 find_gdb_breakpoint_at (CORE_ADDR where)
 {
   struct process_info *proc = current_process ();
@@ -692,6 +710,9 @@ delete_gdb_breakpoint_at (CORE_ADDR addr)
   if (bp == NULL)
     return -1;
 
+  /* Before deleting the breakpoint, make sure to free
+     its condition list.  */
+  clear_gdb_breakpoint_conditions (addr);
   err = delete_breakpoint (bp);
   if (err)
     return -1;
@@ -699,12 +720,126 @@ delete_gdb_breakpoint_at (CORE_ADDR addr)
   return 0;
 }
 
+/* Clear all conditions associated with this breakpoint address.  */
+
+void
+clear_gdb_breakpoint_conditions (CORE_ADDR addr)
+{
+  struct breakpoint *bp = find_gdb_breakpoint_at (addr);
+  struct point_cond_list *cond, **cond_p;
+
+  if (bp == NULL || bp->cond_list == NULL)
+    return;
+
+  cond = bp->cond_list;
+  cond_p = &bp->cond_list->next;
+
+  while (cond != NULL)
+    {
+      free (cond->cond);
+      free (cond);
+      cond = *cond_p;
+      cond_p = &cond->next;
+    }
+
+  bp->cond_list = NULL;
+}
+
+/* Add condition CONDITION to GDBserver's breakpoint BP.  */
+
+void
+add_condition_to_breakpoint (struct breakpoint *bp,
+                            struct agent_expr *condition)
+{
+  struct point_cond_list *new_cond;
+
+  /* Create new condition.  */
+  new_cond = xcalloc (1, sizeof (*new_cond));
+  new_cond->cond = condition;
+
+  /* Add condition to the list.  */
+  new_cond->next = bp->cond_list;
+  bp->cond_list = new_cond;
+}
+
+/* Add a target-side condition CONDITION to the breakpoint at ADDR.  */
+
 int
-gdb_breakpoint_here (CORE_ADDR where)
+add_breakpoint_condition (CORE_ADDR addr, char **condition)
 {
+  struct breakpoint *bp = find_gdb_breakpoint_at (addr);
+  char *actparm = *condition;
+  struct agent_expr *cond;
+
+  if (bp == NULL)
+    return 1;
+
+  if (condition == NULL)
+    return 1;
+
+  cond = gdb_parse_agent_expr (&actparm);
+
+  if (cond == NULL)
+    {
+      fprintf (stderr, "Condition evaluation failed. "
+              "Assuming unconditional.\n");
+      return 0;
+    }
+
+  add_condition_to_breakpoint (bp, cond);
+
+  *condition = actparm;
+
+  return 0;
+}
+
+/* Evaluate condition (if any) at breakpoint BP.  Return 1 if
+   true and 0 otherwise.  */
+
+int
+gdb_condition_true_at_breakpoint (CORE_ADDR where)
+{
+  /* Fetch registers for the current inferior.  */
   struct breakpoint *bp = find_gdb_breakpoint_at (where);
+  ULONGEST value = 0;
+  struct point_cond_list *cl;
+  int err = 0;
+
+  struct regcache *regcache = get_thread_regcache (current_inferior, 1);
+
+  if (bp == NULL)
+    return 0;
 
-  return (bp != NULL);
+  /* Check if the breakpoint is unconditional.  If it is,
+     the condition always evaluates to TRUE.  */
+  if (bp->cond_list == NULL)
+    return 1;
+
+  /* Evaluate each condition in the breakpoint's list of conditions.
+     Return true if any of the conditions evaluates to TRUE.
+
+     If we failed to evaluate the expression, TRUE is returned.  This
+     forces GDB to reevaluate the conditions.  */
+  for (cl = bp->cond_list;
+       cl && !value && !err; cl = cl->next)
+    {
+      /* Evaluate the condition.  */
+      err = gdb_eval_agent_expr (regcache, NULL, cl->cond, &value);
+    }
+
+  if (err)
+    return 1;
+
+  return (value != 0);
+}
+
+/* Return 1 if there is a breakpoint inserted in address WHERE
+   and if its condition, if it exists, is true.  */
+
+int
+gdb_breakpoint_here (CORE_ADDR where)
+{
+  return (find_gdb_breakpoint_at (where) != NULL);
 }
 
 void
index 16b6dec869878bab34d6927a6a2b3de3ebe18cb9..95b7f9dcc5a70857d661cebb07515c6dd893745c 100644 (file)
 struct breakpoint;
 struct fast_tracepoint_jump;
 
+/* Locate a breakpoint placed at address WHERE and return a pointer
+   to its structure.  */
+
+struct breakpoint *find_gdb_breakpoint_at (CORE_ADDR where);
+
 /* Create a new GDB breakpoint at WHERE.  Returns -1 if breakpoints
    are not supported on this target, 0 otherwise.  */
 
@@ -39,6 +44,19 @@ int breakpoint_here (CORE_ADDR addr);
 
 int breakpoint_inserted_here (CORE_ADDR addr);
 
+/* Clear all breakpoint conditions associated with this address.  */
+
+void clear_gdb_breakpoint_conditions (CORE_ADDR addr);
+
+/* Set target-side condition CONDITION to the breakpoint at ADDR.  */
+
+int add_breakpoint_condition (CORE_ADDR addr, char **condition);
+
+/* Evaluation condition (if any) at breakpoint BP.  Return 1 if
+   true and 0 otherwise.  */
+
+int gdb_condition_true_at_breakpoint (CORE_ADDR where);
+
 /* Returns TRUE if there's a GDB breakpoint set at ADDR.  */
 
 int gdb_breakpoint_here (CORE_ADDR where);
index 37dc8d13f9521a523e6976c3f5dc25c8c2875eed..e3d1f7cc371d3c2ac60c89c7998093c9cbe8d0e9 100644 (file)
@@ -1621,6 +1621,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
          strcat (own_buf, ";tracenz+");
        }
 
+      /* Support target-side breakpoint conditions.  */
+      strcat (own_buf, ";ConditionalBreakpoints+");
+
       return;
     }
 
@@ -2825,6 +2828,43 @@ main (int argc, char *argv[])
     }
 }
 
+/* Process options coming from Z packets for *point at address
+   POINT_ADDR.  PACKET is the packet buffer.  *PACKET is updated
+   to point to the first char after the last processed option.  */
+
+static void
+process_point_options (CORE_ADDR point_addr, char **packet)
+{
+  char *dataptr = *packet;
+
+  /* Check if data has the correct format.  */
+  if (*dataptr != ';')
+    return;
+
+  dataptr++;
+
+  while (*dataptr)
+    {
+      switch (*dataptr)
+       {
+         case 'X':
+           /* Conditional expression.  */
+           fprintf (stderr, "Found breakpoint condition.\n");
+           add_breakpoint_condition (point_addr, &dataptr);
+           break;
+         default:
+           /* Unrecognized token, just skip it.  */
+           fprintf (stderr, "Unknown token %c, ignoring.\n",
+                    *dataptr);
+       }
+
+      /* Skip tokens until we find one that we recognize.  */
+      while (*dataptr && *dataptr != 'X' && *dataptr != ';')
+       dataptr++;
+    }
+  *packet = dataptr;
+}
+
 /* Event loop callback that handles a serial event.  The first byte in
    the serial buffer gets us here.  We expect characters to arrive at
    a brisk pace, so we read the rest of the packet with a blocking
@@ -3147,7 +3187,22 @@ process_serial_event (void)
          case '4': /* access watchpoint */
            require_running (own_buf);
            if (insert && the_target->insert_point != NULL)
-             res = (*the_target->insert_point) (type, addr, len);
+             {
+               /* Insert the breakpoint.  If it is already inserted, nothing
+                  will take place.  */
+               res = (*the_target->insert_point) (type, addr, len);
+
+               /* GDB may have sent us a list of *point parameters to be
+                  evaluated on the target's side.  Read such list here.  If we
+                  already have a list of parameters, GDB is telling us to drop
+                  that list and use this one instead.  */
+               if (!res && (type == '0' || type == '1'))
+                 {
+                   /* Remove previous conditions.  */
+                   clear_gdb_breakpoint_conditions (addr);
+                   process_point_options (addr, &dataptr);
+                 }
+             }
            else if (!insert && the_target->remove_point != NULL)
              res = (*the_target->remove_point) (type, addr, len);
            break;