Use memmove to copy overlap memory.
[deliverable/binutils-gdb.git] / gdb / tracepoint.c
index 550a880d0de18bf11b8faea881de9290dcbd7709..ae0e040e53cf7433bdf94021656a922da12f6668 100644 (file)
@@ -153,6 +153,11 @@ char *default_collect = "";
 
 static int disconnected_tracing;
 
+/* This variable controls whether we ask the target for a linear or
+   circular trace buffer.  */
+
+static int circular_trace_buffer;
+
 /* ======= Important command functions: ======= */
 static void trace_actions_command (char *, int);
 static void trace_start_command (char *, int);
@@ -164,7 +169,6 @@ static void trace_find_tracepoint_command (char *, int);
 static void trace_find_line_command (char *, int);
 static void trace_find_range_command (char *, int);
 static void trace_find_outside_command (char *, int);
-static void tracepoint_save_command (char *, int);
 static void trace_dump_command (char *, int);
 
 /* support routines */
@@ -174,7 +178,6 @@ static void add_aexpr (struct collection_list *, struct agent_expr *);
 static char *mem2hex (gdb_byte *, char *, int);
 static void add_register (struct collection_list *collection,
                          unsigned int regno);
-static struct cleanup *make_cleanup_free_actions (struct breakpoint *t);
 
 extern void send_disconnected_tracing_value (int value);
 
@@ -192,7 +195,8 @@ char *stop_reason_names[] = {
   "tstop",
   "tfull",
   "tdisconnected",
-  "tpasscount"
+  "tpasscount",
+  "terror"
 };
 
 struct trace_status *
@@ -273,7 +277,7 @@ create_trace_state_variable (const char *name)
   struct trace_state_variable tsv;
 
   memset (&tsv, 0, sizeof (tsv));
-  tsv.name = name;
+  tsv.name = xstrdup (name);
   tsv.number = next_tsv_number++;
   return VEC_safe_push (tsv_s, tvariables, &tsv);
 }
@@ -302,6 +306,7 @@ delete_trace_state_variable (const char *name)
   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
     if (strcmp (name, tsv->name) == 0)
       {
+       xfree ((void *)tsv->name);
        VEC_unordered_remove (tsv_s, tvariables, ix);
        return;
       }
@@ -403,51 +408,78 @@ delete_trace_variable_command (char *args, int from_tty)
   dont_repeat ();
 }
 
-/* List all the trace state variables.  */
-
-static void
-tvariables_info (char *args, int from_tty)
+void
+tvariables_info_1 (void)
 {
   struct trace_state_variable *tsv;
   int ix;
-  char *reply;
-  ULONGEST tval;
+  int count = 0;
+  struct cleanup *back_to;
 
-  if (VEC_length (tsv_s, tvariables) == 0)
+  if (VEC_length (tsv_s, tvariables) == 0 && !ui_out_is_mi_like_p (uiout))
     {
       printf_filtered (_("No trace state variables.\n"));
       return;
     }
 
   /* Try to acquire values from the target.  */
-  for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+  for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix, ++count)
     tsv->value_known = target_get_trace_state_variable_value (tsv->number,
                                                              &(tsv->value));
 
-  printf_filtered (_("Name\t\t  Initial\tCurrent\n"));
+  back_to = make_cleanup_ui_out_table_begin_end (uiout, 3,
+                                                 count, "trace-variables");
+  ui_out_table_header (uiout, 15, ui_left, "name", "Name");
+  ui_out_table_header (uiout, 11, ui_left, "initial", "Initial");
+  ui_out_table_header (uiout, 11, ui_left, "current", "Current");
+
+  ui_out_table_body (uiout);
 
   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
     {
-      printf_filtered ("$%s", tsv->name);
-      print_spaces_filtered (17 - strlen (tsv->name), gdb_stdout);
-      printf_filtered ("%s ", plongest (tsv->initial_value));
-      print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout);
+      struct cleanup *back_to2;
+      char *c;
+      char *name;
+
+      back_to2 = make_cleanup_ui_out_tuple_begin_end (uiout, "variable");
+
+      name = concat ("$", tsv->name, NULL);
+      make_cleanup (xfree, name);
+      ui_out_field_string (uiout, "name", name);
+      ui_out_field_string (uiout, "initial", plongest (tsv->initial_value));
+
       if (tsv->value_known)
-       printf_filtered ("  %s", plongest (tsv->value));
+        c = plongest (tsv->value);
+      else if (ui_out_is_mi_like_p (uiout))
+        /* For MI, we prefer not to use magic string constants, but rather
+           omit the field completely.  The difference between unknown and
+           undefined does not seem important enough to represent.  */
+        c = NULL;
       else if (current_trace_status ()->running || traceframe_number >= 0)
        /* The value is/was defined, but we don't have it.  */
-       printf_filtered (_("  <unknown>"));
+        c = "<unknown>";
       else
        /* It is not meaningful to ask about the value.  */
-       printf_filtered (_("  <undefined>"));
-      printf_filtered ("\n");
+        c = "<undefined>";
+      if (c)
+        ui_out_field_string (uiout, "current", c);
+      ui_out_text (uiout, "\n");
+
+      do_cleanups (back_to2);
     }
+
+  do_cleanups (back_to);
 }
 
-/* ACTIONS functions: */
+/* List all the trace state variables.  */
 
-/* Prototypes for action-parsing utility commands  */
-static void read_actions (struct breakpoint *);
+static void
+tvariables_info (char *args, int from_tty)
+{
+  tvariables_info_1 ();
+}
+
+/* ACTIONS functions: */
 
 /* The three functions:
    collect_pseudocommand, 
@@ -487,138 +519,23 @@ static void
 trace_actions_command (char *args, int from_tty)
 {
   struct breakpoint *t;
-  char tmpbuf[128];
-  char *end_msg = "End with a line saying just \"end\".";
+  struct command_line *l;
 
   t = get_tracepoint_by_number (&args, 0, 1);
   if (t)
     {
-      sprintf (tmpbuf, "Enter actions for tracepoint %d, one per line.",
-              t->number);
-
-      if (from_tty)
-       {
-         if (deprecated_readline_begin_hook)
-           (*deprecated_readline_begin_hook) ("%s  %s\n", tmpbuf, end_msg);
-         else if (input_from_terminal_p ())
-           printf_filtered ("%s\n%s\n", tmpbuf, end_msg);
-       }
-
-      free_actions (t);
-      t->step_count = 0;       /* read_actions may set this */
-      read_actions (t);
+      char *tmpbuf =
+       xstrprintf ("Enter actions for tracepoint %d, one per line.",
+                   t->number);
+      struct cleanup *cleanups = make_cleanup (xfree, tmpbuf);
 
-      if (deprecated_readline_end_hook)
-       (*deprecated_readline_end_hook) ();
-      /* tracepoints_changed () */
+      l = read_command_lines (tmpbuf, from_tty, 1, check_tracepoint_command, t);
+      do_cleanups (cleanups);
+      breakpoint_set_commands (t, l);
     }
   /* else just return */
 }
 
-/* worker function */
-static void
-read_actions (struct breakpoint *t)
-{
-  char *line;
-  char *prompt1 = "> ", *prompt2 = "  > ";
-  char *prompt = prompt1;
-  enum actionline_type linetype;
-  extern FILE *instream;
-  struct action_line *next = NULL, *temp;
-  struct cleanup *old_chain;
-
-  /* Control-C quits instantly if typed while in this loop
-     since it should not wait until the user types a newline.  */
-  immediate_quit++;
-  /* FIXME: kettenis/20010823: Something is wrong here.  In this file
-     STOP_SIGNAL is never defined.  So this code has been left out, at
-     least for quite a while now.  Replacing STOP_SIGNAL with SIGTSTP
-     leads to compilation failures since the variable job_control
-     isn't declared.  Leave this alone for now.  */
-#ifdef STOP_SIGNAL
-  if (job_control)
-    signal (STOP_SIGNAL, handle_stop_sig);
-#endif
-  old_chain = make_cleanup_free_actions (t);
-  while (1)
-    {
-      /* Make sure that all output has been output.  Some machines may
-         let you get away with leaving out some of the gdb_flush, but
-         not all.  */
-      wrap_here ("");
-      gdb_flush (gdb_stdout);
-      gdb_flush (gdb_stderr);
-
-      if (deprecated_readline_hook && instream == NULL)
-       line = (*deprecated_readline_hook) (prompt);
-      else if (instream == stdin && ISATTY (instream))
-       {
-         line = gdb_readline_wrapper (prompt);
-         if (line && *line)    /* add it to command history */
-           add_history (line);
-       }
-      else
-       line = gdb_readline (0);
-
-      if (!line)
-        {
-          line = xstrdup ("end");
-          printf_filtered ("end\n");
-        }
-      
-      linetype = validate_actionline (&line, t);
-      if (linetype == BADLINE)
-       continue;               /* already warned -- collect another line */
-
-      temp = xmalloc (sizeof (struct action_line));
-      temp->next = NULL;
-      temp->action = line;
-
-      if (next == NULL)                /* first action for this tracepoint? */
-       t->actions = next = temp;
-      else
-       {
-         next->next = temp;
-         next = temp;
-       }
-
-      if (linetype == STEPPING)        /* begin "while-stepping" */
-       {
-         if (prompt == prompt2)
-           {
-             warning (_("Already processing 'while-stepping'"));
-             continue;
-           }
-         else
-           prompt = prompt2;   /* change prompt for stepping actions */
-       }
-      else if (linetype == END)
-       {
-         if (prompt == prompt2)
-           {
-             prompt = prompt1; /* end of single-stepping actions */
-           }
-         else
-           {                   /* end of actions */
-             if (t->actions->next == NULL)
-               {
-                 /* An "end" all by itself with no other actions
-                    means this tracepoint has no actions.
-                    Discard empty list.  */
-                 free_actions (t);
-               }
-             break;
-           }
-       }
-    }
-#ifdef STOP_SIGNAL
-  if (job_control)
-    signal (STOP_SIGNAL, SIG_DFL);
-#endif
-  immediate_quit--;
-  discard_cleanups (old_chain);
-}
-
 /* worker function */
 enum actionline_type
 validate_actionline (char **line, struct breakpoint *t)
@@ -626,7 +543,8 @@ validate_actionline (char **line, struct breakpoint *t)
   struct cmd_list_element *c;
   struct expression *exp = NULL;
   struct cleanup *old_chain = NULL;
-  char *p;
+  char *p, *tmp_p;
+  struct bp_location *loc;
 
   /* if EOF is typed, *line is NULL */
   if (*line == NULL)
@@ -672,48 +590,53 @@ validate_actionline (char **line, struct breakpoint *t)
                }
              /* else fall thru, treat p as an expression and parse it!  */
            }
-         exp = parse_exp_1 (&p, block_for_pc (t->loc->address), 1);
-         old_chain = make_cleanup (free_current_contents, &exp);
-
-         if (exp->elts[0].opcode == OP_VAR_VALUE)
+         tmp_p = p;
+         for (loc = t->loc; loc; loc = loc->next)
            {
-             if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST)
-               {
-                 warning (_("constant %s (value %ld) will not be collected."),
-                          SYMBOL_PRINT_NAME (exp->elts[2].symbol),
-                          SYMBOL_VALUE (exp->elts[2].symbol));
-                 return BADLINE;
-               }
-             else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT)
+             p = tmp_p;
+             exp = parse_exp_1 (&p, block_for_pc (loc->address), 1);
+             old_chain = make_cleanup (free_current_contents, &exp);
+
+             if (exp->elts[0].opcode == OP_VAR_VALUE)
                {
-                 warning (_("%s is optimized away and cannot be collected."),
-                          SYMBOL_PRINT_NAME (exp->elts[2].symbol));
-                 return BADLINE;
+                 if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_CONST)
+                   {
+                     warning (_("constant %s (value %ld) will not be collected."),
+                              SYMBOL_PRINT_NAME (exp->elts[2].symbol),
+                              SYMBOL_VALUE (exp->elts[2].symbol));
+                     return BADLINE;
+                   }
+                 else if (SYMBOL_CLASS (exp->elts[2].symbol) == LOC_OPTIMIZED_OUT)
+                   {
+                     warning (_("%s is optimized away and cannot be collected."),
+                              SYMBOL_PRINT_NAME (exp->elts[2].symbol));
+                     return BADLINE;
+                   }
                }
-           }
 
-         /* We have something to collect, make sure that the expr to
-            bytecode translator can handle it and that it's not too
-            long.  */
-         aexpr = gen_trace_for_expr (t->loc->address, exp);
-         make_cleanup_free_agent_expr (aexpr);
+             /* We have something to collect, make sure that the expr to
+                bytecode translator can handle it and that it's not too
+                long.  */
+             aexpr = gen_trace_for_expr (loc->address, exp);
+             make_cleanup_free_agent_expr (aexpr);
 
-         if (aexpr->len > MAX_AGENT_EXPR_LEN)
-           error (_("expression too complicated, try simplifying"));
+             if (aexpr->len > MAX_AGENT_EXPR_LEN)
+               error (_("expression too complicated, try simplifying"));
 
-         ax_reqs (aexpr, &areqs);
-         (void) make_cleanup (xfree, areqs.reg_mask);
+             ax_reqs (aexpr, &areqs);
+             (void) make_cleanup (xfree, areqs.reg_mask);
 
-         if (areqs.flaw != agent_flaw_none)
-           error (_("malformed expression"));
+             if (areqs.flaw != agent_flaw_none)
+               error (_("malformed expression"));
 
-         if (areqs.min_height < 0)
-           error (_("gdb: Internal error: expression has min height < 0"));
+             if (areqs.min_height < 0)
+               error (_("gdb: Internal error: expression has min height < 0"));
 
-         if (areqs.max_height > 20)
-           error (_("expression too complicated, try simplifying"));
+             if (areqs.max_height > 20)
+               error (_("expression too complicated, try simplifying"));
 
-         do_cleanups (old_chain);
+             do_cleanups (old_chain);
+           }
        }
       while (p && *p++ == ',');
       return GENERIC;
@@ -728,20 +651,25 @@ validate_actionline (char **line, struct breakpoint *t)
          while (isspace ((int) *p))
            p++;
 
-         /* Only expressions are allowed for this action.  */
-         exp = parse_exp_1 (&p, block_for_pc (t->loc->address), 1);
-         old_chain = make_cleanup (free_current_contents, &exp);
+         tmp_p = p;
+         for (loc = t->loc; loc; loc = loc->next)
+           {
+             p = tmp_p;
+             /* Only expressions are allowed for this action.  */
+             exp = parse_exp_1 (&p, block_for_pc (loc->address), 1);
+             old_chain = make_cleanup (free_current_contents, &exp);
 
-         /* We have something to evaluate, make sure that the expr to
-            bytecode translator can handle it and that it's not too
-            long.  */
-         aexpr = gen_eval_for_expr (t->loc->address, exp);
-         make_cleanup_free_agent_expr (aexpr);
+             /* We have something to evaluate, make sure that the expr to
+                bytecode translator can handle it and that it's not too
+                long.  */
+             aexpr = gen_eval_for_expr (loc->address, exp);
+             make_cleanup_free_agent_expr (aexpr);
 
-         if (aexpr->len > MAX_AGENT_EXPR_LEN)
-           error (_("expression too complicated, try simplifying"));
+             if (aexpr->len > MAX_AGENT_EXPR_LEN)
+               error (_("expression too complicated, try simplifying"));
 
-         do_cleanups (old_chain);
+             do_cleanups (old_chain);
+           }
        }
       while (p && *p++ == ',');
       return GENERIC;
@@ -757,8 +685,7 @@ validate_actionline (char **line, struct breakpoint *t)
       if (*p == '\0' ||
          (t->step_count = strtol (p, &p, 0)) == 0)
        {
-         warning (_("'%s': bad step-count; command ignored."), *line);
-         return BADLINE;
+         error (_("'%s': bad step-count."), *line);
        }
       return STEPPING;
     }
@@ -766,39 +693,10 @@ validate_actionline (char **line, struct breakpoint *t)
     return END;
   else
     {
-      warning (_("'%s' is not a supported tracepoint action."), *line);
-      return BADLINE;
+      error (_("'%s' is not a supported tracepoint action."), *line);
     }
 }
 
-/* worker function */
-void
-free_actions (struct breakpoint *t)
-{
-  struct action_line *line, *next;
-
-  for (line = t->actions; line; line = next)
-    {
-      next = line->next;
-      if (line->action)
-       xfree (line->action);
-      xfree (line);
-    }
-  t->actions = NULL;
-}
-
-static void
-do_free_actions_cleanup (void *t)
-{
-  free_actions (t);
-}
-
-static struct cleanup *
-make_cleanup_free_actions (struct breakpoint *t)
-{
-  return make_cleanup (do_free_actions_cleanup, t);
-}
-
 enum {
   memrange_absolute = -1
 };
@@ -939,6 +837,7 @@ collect_symbol (struct collection_list *collect,
   unsigned long len;
   unsigned int reg;
   bfd_signed_vma offset;
+  int treat_as_expr = 0;
 
   len = TYPE_LENGTH (check_typedef (SYMBOL_TYPE (sym)));
   switch (SYMBOL_CLASS (sym))
@@ -963,7 +862,12 @@ collect_symbol (struct collection_list *collect,
                           SYMBOL_PRINT_NAME (sym), len,
                           tmp /* address */);
        }
-      add_memrange (collect, memrange_absolute, offset, len);
+      /* A struct may be a C++ class with static fields, go to general
+        expression handling.  */
+      if (TYPE_CODE (SYMBOL_TYPE (sym)) == TYPE_CODE_STRUCT)
+       treat_as_expr = 1;
+      else
+       add_memrange (collect, memrange_absolute, offset, len);
       break;
     case LOC_REGISTER:
       reg = SYMBOL_REGISTER_OPS (sym)->register_number (sym, gdbarch);
@@ -1018,59 +922,73 @@ collect_symbol (struct collection_list *collect,
        }
       add_memrange (collect, reg, offset, len);
       break;
+
     case LOC_UNRESOLVED:
-      printf_filtered ("Don't know LOC_UNRESOLVED %s\n", 
-                      SYMBOL_PRINT_NAME (sym));
+      treat_as_expr = 1;
       break;
+
     case LOC_OPTIMIZED_OUT:
       printf_filtered ("%s has been optimized out of existence.\n",
                       SYMBOL_PRINT_NAME (sym));
       break;
 
     case LOC_COMPUTED:
-      {
-       struct agent_expr *aexpr;
-       struct cleanup *old_chain1 = NULL;
-       struct agent_reqs areqs;
-
-       aexpr = gen_trace_for_var (scope, sym);
-
-       old_chain1 = make_cleanup_free_agent_expr (aexpr);
-
-       ax_reqs (aexpr, &areqs);
-       if (areqs.flaw != agent_flaw_none)
-         error (_("malformed expression"));
-       
-       if (areqs.min_height < 0)
-         error (_("gdb: Internal error: expression has min height < 0"));
-       if (areqs.max_height > 20)
-         error (_("expression too complicated, try simplifying"));
-
-       discard_cleanups (old_chain1);
-       add_aexpr (collect, aexpr);
-
-       /* take care of the registers */
-       if (areqs.reg_mask_len > 0)
-         {
-           int ndx1, ndx2;
-
-           for (ndx1 = 0; ndx1 < areqs.reg_mask_len; ndx1++)
-             {
-               QUIT;   /* allow user to bail out with ^C */
-               if (areqs.reg_mask[ndx1] != 0)
-                 {
-                   /* assume chars have 8 bits */
-                   for (ndx2 = 0; ndx2 < 8; ndx2++)
-                     if (areqs.reg_mask[ndx1] & (1 << ndx2))
-                       /* it's used -- record it */
-                       add_register (collect, 
-                                     ndx1 * 8 + ndx2);
-                 }
-             }
-         }
-      }
+      treat_as_expr = 1;
       break;
     }
+
+  /* Expressions are the most general case.  */
+  if (treat_as_expr)
+    {
+      struct agent_expr *aexpr;
+      struct cleanup *old_chain1 = NULL;
+      struct agent_reqs areqs;
+
+      aexpr = gen_trace_for_var (scope, gdbarch, sym);
+
+      /* It can happen that the symbol is recorded as a computed
+        location, but it's been optimized away and doesn't actually
+        have a location expression.  */
+      if (!aexpr)
+       {
+         printf_filtered ("%s has been optimized out of existence.\n",
+                          SYMBOL_PRINT_NAME (sym));
+         return;
+       }
+
+      old_chain1 = make_cleanup_free_agent_expr (aexpr);
+
+      ax_reqs (aexpr, &areqs);
+      if (areqs.flaw != agent_flaw_none)
+       error (_("malformed expression"));
+      
+      if (areqs.min_height < 0)
+       error (_("gdb: Internal error: expression has min height < 0"));
+      if (areqs.max_height > 20)
+       error (_("expression too complicated, try simplifying"));
+
+      discard_cleanups (old_chain1);
+      add_aexpr (collect, aexpr);
+
+      /* take care of the registers */
+      if (areqs.reg_mask_len > 0)
+       {
+         int ndx1, ndx2;
+
+         for (ndx1 = 0; ndx1 < areqs.reg_mask_len; ndx1++)
+           {
+             QUIT;     /* allow user to bail out with ^C */
+             if (areqs.reg_mask[ndx1] != 0)
+               {
+                 /* assume chars have 8 bits */
+                 for (ndx2 = 0; ndx2 < 8; ndx2++)
+                   if (areqs.reg_mask[ndx1] & (1 << ndx2))
+                     /* it's used -- record it */
+                     add_register (collect, ndx1 * 8 + ndx2);
+               }
+           }
+       }
+    }
 }
 
 /* Add all locals (or args) symbols to collection list */
@@ -1237,70 +1155,31 @@ stringify_collection_list (struct collection_list *list, char *string)
     return *str_list;
 }
 
-/* Render all actions into gdb protocol.  */
-/*static*/ void
-encode_actions (struct breakpoint *t, char ***tdp_actions,
-               char ***stepping_actions)
+
+static void
+encode_actions_1 (struct command_line *action,
+                 struct breakpoint *t,
+                 struct bp_location *tloc,
+                 int frame_reg,
+                 LONGEST frame_offset,
+                 struct collection_list *collect,
+                 struct collection_list *stepping_list)
 {
-  static char tdp_buff[2048], step_buff[2048];
   char *action_exp;
   struct expression *exp = NULL;
-  struct action_line *action;
+  struct command_line *actions;
   int i;
   struct value *tempval;
-  struct collection_list *collect;
   struct cmd_list_element *cmd;
   struct agent_expr *aexpr;
-  int frame_reg;
-  LONGEST frame_offset;
-  char *default_collect_line = NULL;
-  struct action_line *default_collect_action = NULL;
-
-  clear_collection_list (&tracepoint_list);
-  clear_collection_list (&stepping_list);
-  collect = &tracepoint_list;
-
-  *tdp_actions = NULL;
-  *stepping_actions = NULL;
-
-  gdbarch_virtual_frame_pointer (t->gdbarch,
-                                t->loc->address, &frame_reg, &frame_offset);
-
-  action = t->actions;
-
-  /* If there are default expressions to collect, make up a collect
-     action and prepend to the action list to encode.  Note that since
-     validation is per-tracepoint (local var "xyz" might be valid for
-     one tracepoint and not another, etc), we make up the action on
-     the fly, and don't cache it.  */
-  if (*default_collect)
-    {
-      char *line;
-      enum actionline_type linetype;
-
-      default_collect_line = xmalloc (12 + strlen (default_collect));
-      sprintf (default_collect_line, "collect %s", default_collect);
-      line = default_collect_line;
-      linetype = validate_actionline (&line, t);
-      if (linetype != BADLINE)
-       {
-         default_collect_action = xmalloc (sizeof (struct action_line));
-         default_collect_action->next = t->actions;
-         default_collect_action->action = line;
-         action = default_collect_action;
-       }
-    }
 
   for (; action; action = action->next)
     {
       QUIT;                    /* allow user to bail out with ^C */
-      action_exp = action->action;
+      action_exp = action->line;
       while (isspace ((int) *action_exp))
        action_exp++;
 
-      if (*action_exp == '#')  /* comment line */
-       return;
-
       cmd = lookup_cmd (&action_exp, cmdlist, "", -1, 1);
       if (cmd == 0)
        error (_("Bad action list item: %s"), action_exp);
@@ -1323,7 +1202,7 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
                {
                  add_local_symbols (collect,
                                     t->gdbarch,
-                                    t->loc->address,
+                                    tloc->address,
                                     frame_reg,
                                     frame_offset,
                                     'A');
@@ -1333,7 +1212,7 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
                {
                  add_local_symbols (collect,
                                     t->gdbarch,
-                                    t->loc->address,
+                                    tloc->address,
                                     frame_reg,
                                     frame_offset,
                                     'L');
@@ -1347,7 +1226,7 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
                  struct agent_reqs areqs;
 
                  exp = parse_exp_1 (&action_exp, 
-                                    block_for_pc (t->loc->address), 1);
+                                    block_for_pc (tloc->address), 1);
                  old_chain = make_cleanup (free_current_contents, &exp);
 
                  switch (exp->elts[0].opcode)
@@ -1382,11 +1261,11 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
                                      t->gdbarch,
                                      frame_reg,
                                      frame_offset,
-                                     t->loc->address);
+                                     tloc->address);
                      break;
 
                    default:    /* full-fledged expression */
-                     aexpr = gen_trace_for_expr (t->loc->address, exp);
+                     aexpr = gen_trace_for_expr (tloc->address, exp);
 
                      old_chain1 = make_cleanup_free_agent_expr (aexpr);
 
@@ -1444,10 +1323,10 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
                  struct agent_reqs areqs;
 
                  exp = parse_exp_1 (&action_exp, 
-                                    block_for_pc (t->loc->address), 1);
+                                    block_for_pc (tloc->address), 1);
                  old_chain = make_cleanup (free_current_contents, &exp);
 
-                 aexpr = gen_eval_for_expr (t->loc->address, exp);
+                 aexpr = gen_eval_for_expr (tloc->address, exp);
                  old_chain1 = make_cleanup_free_agent_expr (aexpr);
 
                  ax_reqs (aexpr, &areqs);
@@ -1471,26 +1350,81 @@ encode_actions (struct breakpoint *t, char ***tdp_actions,
        }                       /* if */
       else if (cmd_cfunc_eq (cmd, while_stepping_pseudocommand))
        {
-         collect = &stepping_list;
+         /* We check against nested while-stepping when setting
+            breakpoint action, so no way to run into nested
+            here.  */
+         gdb_assert (stepping_list);
+
+         encode_actions_1 (action->body_list[0], t, tloc, frame_reg, frame_offset,
+                           stepping_list, NULL);
        }
-      else if (cmd_cfunc_eq (cmd, end_actions_pseudocommand))
+      else
+       error (_("Invalid tracepoint command '%s'"), action->line);
+    }                          /* for */
+}
+
+/* Render all actions into gdb protocol.  */
+/*static*/ void
+encode_actions (struct breakpoint *t, struct bp_location *tloc,
+               char ***tdp_actions, char ***stepping_actions)
+{
+  static char tdp_buff[2048], step_buff[2048];
+  char *default_collect_line = NULL;
+  struct command_line *actions;
+  struct command_line *default_collect_action = NULL;
+  int frame_reg;
+  LONGEST frame_offset;
+  struct cleanup *back_to;
+
+  back_to = make_cleanup (null_cleanup, NULL);
+
+  clear_collection_list (&tracepoint_list);
+  clear_collection_list (&stepping_list);
+
+  *tdp_actions = NULL;
+  *stepping_actions = NULL;
+
+  gdbarch_virtual_frame_pointer (t->gdbarch,
+                                t->loc->address, &frame_reg, &frame_offset);
+
+  actions = t->commands->commands;
+
+  /* If there are default expressions to collect, make up a collect
+     action and prepend to the action list to encode.  Note that since
+     validation is per-tracepoint (local var "xyz" might be valid for
+     one tracepoint and not another, etc), we make up the action on
+     the fly, and don't cache it.  */
+  if (*default_collect)
+    {
+      char *line;
+      enum actionline_type linetype;
+
+      default_collect_line =  xstrprintf ("collect %s", default_collect);
+      make_cleanup (xfree, default_collect_line);
+
+      line = default_collect_line;
+      linetype = validate_actionline (&line, t);
+      if (linetype != BADLINE)
        {
-         if (collect == &stepping_list)        /* end stepping actions */
-           collect = &tracepoint_list;
-         else
-           break;              /* end tracepoint actions */
+         default_collect_action = xmalloc (sizeof (struct command_line));
+         make_cleanup (xfree, default_collect_action);
+         default_collect_action->next = t->commands->commands;
+         default_collect_action->line = line;
+         actions = default_collect_action;
        }
-    }                          /* for */
+    }
+  encode_actions_1 (actions, t, tloc, frame_reg, frame_offset,
+                   &tracepoint_list, &stepping_list);
+
   memrange_sortmerge (&tracepoint_list);
   memrange_sortmerge (&stepping_list);
 
-  *tdp_actions = stringify_collection_list (&tracepoint_list, 
+  *tdp_actions = stringify_collection_list (&tracepoint_list,
                                            tdp_buff);
-  *stepping_actions = stringify_collection_list (&stepping_list, 
+  *stepping_actions = stringify_collection_list (&stepping_list,
                                                 step_buff);
 
-  xfree (default_collect_line);
-  xfree (default_collect_action);
+  do_cleanups (back_to);
 }
 
 static void
@@ -1507,41 +1441,52 @@ add_aexpr (struct collection_list *collect, struct agent_expr *aexpr)
   collect->next_aexpr_elt++;
 }
 
-/* tstart command:
 
-   Tell target to clear any previous trace experiment.
-   Walk the list of tracepoints, and send them (and their actions)
-   to the target.  If no errors, 
-   Tell target to start a new trace experiment.  */
-
-static void
-trace_start_command (char *args, int from_tty)
+void
+start_tracing (void)
 {
   char buf[2048];
   VEC(breakpoint_p) *tp_vec = NULL;
   int ix;
   struct breakpoint *t;
   struct trace_state_variable *tsv;
-  int any_downloaded = 0;
+  int any_enabled = 0;
+  
+  tp_vec = all_tracepoints ();
 
-  dont_repeat ();      /* Like "run", dangerous to repeat accidentally.  */
+  /* No point in tracing without any tracepoints... */
+  if (VEC_length (breakpoint_p, tp_vec) == 0)
+    {
+      VEC_free (breakpoint_p, tp_vec);
+      error (_("No tracepoints defined, not starting trace"));
+    }
+
+  for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+    {
+      if (t->enable_state == bp_enabled)
+       {
+         any_enabled = 1;
+         break;
+       }
+    }
+
+  /* No point in tracing with only disabled tracepoints.  */
+  if (!any_enabled)
+    {
+      VEC_free (breakpoint_p, tp_vec);
+      error (_("No tracepoints enabled, not starting trace"));
+    }
 
   target_trace_init ();
-  
-  tp_vec = all_tracepoints ();
+
   for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
     {
       t->number_on_target = 0;
       target_download_tracepoint (t);
       t->number_on_target = t->number;
-      any_downloaded = 1;
     }
   VEC_free (breakpoint_p, tp_vec);
-  
-  /* No point in tracing without any tracepoints... */
-  if (!any_downloaded)
-    error ("No tracepoints downloaded, not starting trace");
-  
+
   /* Send down all the trace state variables too.  */
   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
     {
@@ -1550,6 +1495,9 @@ trace_start_command (char *args, int from_tty)
   
   /* Tell target to treat text-like sections as transparent.  */
   target_trace_set_readonly_regions ();
+  /* Set some mode flags.  */
+  target_set_disconnected_tracing (disconnected_tracing);
+  target_set_circular_trace_buffer (circular_trace_buffer);
 
   /* Now insert traps and begin collecting data.  */
   target_trace_start ();
@@ -1561,6 +1509,21 @@ trace_start_command (char *args, int from_tty)
   current_trace_status()->running = 1;
 }
 
+/* tstart command:
+
+   Tell target to clear any previous trace experiment.
+   Walk the list of tracepoints, and send them (and their actions)
+   to the target.  If no errors,
+   Tell target to start a new trace experiment.  */
+
+static void
+trace_start_command (char *args, int from_tty)
+{
+  dont_repeat ();      /* Like "run", dangerous to repeat accidentally.  */
+
+  start_tracing ();
+}
+
 /* tstop command */
 static void
 trace_stop_command (char *args, int from_tty)
@@ -1569,7 +1532,7 @@ trace_stop_command (char *args, int from_tty)
 }
 
 void
-stop_tracing ()
+stop_tracing (void)
 {
   target_trace_stop ();
   /* should change in response to reply? */
@@ -1625,10 +1588,17 @@ trace_status_command (char *args, int from_tty)
          printf_filtered (_("Trace stopped because of disconnection.\n"));
          break;
        case tracepoint_passcount:
-         /* FIXME account for number on target */
          printf_filtered (_("Trace stopped by tracepoint %d.\n"),
                           ts->stopping_tracepoint);
          break;
+       case tracepoint_error:
+         if (ts->stopping_tracepoint)
+           printf_filtered (_("Trace stopped by an error (%s, tracepoint %d).\n"),
+                            ts->error_desc, ts->stopping_tracepoint);
+         else
+           printf_filtered (_("Trace stopped by an error (%s).\n"),
+                            ts->error_desc);
+         break;
        case trace_stop_reason_unknown:
          printf_filtered (_("Trace stopped for an unknown reason.\n"));
          break;
@@ -1639,16 +1609,34 @@ trace_status_command (char *args, int from_tty)
        }
     }
 
-  if (ts->traceframe_count >= 0)
+  if (ts->traceframes_created >= 0
+      && ts->traceframe_count != ts->traceframes_created)
+    {
+      printf_filtered (_("Buffer contains %d trace frames (of %d created total).\n"),
+                      ts->traceframe_count, ts->traceframes_created);
+    }
+  else if (ts->traceframe_count >= 0)
     {
       printf_filtered (_("Collected %d trace frames.\n"),
                       ts->traceframe_count);
     }
 
-  if (ts->buffer_free)
+  if (ts->buffer_free >= 0)
     {
-      printf_filtered (_("Trace buffer has %d bytes free.\n"),
-                      ts->buffer_free);
+      if (ts->buffer_size >= 0)
+       {
+         printf_filtered (_("Trace buffer has %d bytes of %d bytes free"),
+                          ts->buffer_free, ts->buffer_size);
+         if (ts->buffer_size > 0)
+           printf_filtered (_(" (%d%% full)"),
+                            ((int) ((((long long) (ts->buffer_size
+                                                   - ts->buffer_free)) * 100)
+                                    / ts->buffer_size)));
+         printf_filtered (_(".\n"));
+       }
+      else
+       printf_filtered (_("Trace buffer has %d bytes free.\n"),
+                        ts->buffer_free);
     }
 
   /* Now report on what we're doing with tfind.  */
@@ -1659,6 +1647,101 @@ trace_status_command (char *args, int from_tty)
     printf_filtered (_("Not looking at any trace frame.\n"));
 }
 
+/* Report the trace status to uiout, in a way suitable for MI, and not
+   suitable for CLI.  If ON_STOP is true, suppress a few fields that
+   are not meaningful in the -trace-stop response.
+
+   The implementation is essentially parallel to trace_status_command, but
+   merging them will result in unreadable code.  */
+void
+trace_status_mi (int on_stop)
+{
+  struct trace_status *ts = current_trace_status ();
+  int status;
+  char *string_status;
+
+  status = target_get_trace_status (ts);
+
+  if (status == -1 && !ts->from_file)
+    {
+      ui_out_field_string (uiout, "supported", "0");
+      return;
+    }
+
+  if (ts->from_file)
+    ui_out_field_string (uiout, "supported", "file");
+  else if (!on_stop)
+    ui_out_field_string (uiout, "supported", "1");
+
+  gdb_assert (ts->running_known);
+
+  if (ts->running)
+    {
+      ui_out_field_string (uiout, "running", "1");
+
+      /* Unlike CLI, do not show the state of 'disconnected-tracing' variable.
+        Given that the frontend gets the status either on -trace-stop, or from
+        -trace-status after re-connection, it does not seem like this
+        information is necessary for anything.  It is not necessary for either
+        figuring the vital state of the target nor for navigation of trace
+        frames.  If the frontend wants to show the current state is some
+        configure dialog, it can request the value when such dialog is
+        invoked by the user.  */
+    }
+  else
+    {
+      char *stop_reason = NULL;
+      int stopping_tracepoint = -1;
+
+      if (!on_stop)
+       ui_out_field_string (uiout, "running", "0");
+
+      if (ts->stop_reason != trace_stop_reason_unknown)
+       {
+         switch (ts->stop_reason)
+           {
+           case tstop_command:
+             stop_reason = "request";
+             break;
+           case trace_buffer_full:
+             stop_reason = "overflow";
+             break;
+           case trace_disconnected:
+             stop_reason = "disconnection";
+             break;
+           case tracepoint_passcount:
+             stop_reason = "passcount";
+             stopping_tracepoint = ts->stopping_tracepoint;
+             break;
+           case tracepoint_error:
+             stop_reason = "error";
+             stopping_tracepoint = ts->stopping_tracepoint;
+             break;
+           }
+         
+         if (stop_reason)
+           {
+             ui_out_field_string (uiout, "stop-reason", stop_reason);
+             if (stopping_tracepoint != -1)
+               ui_out_field_int (uiout, "stopping-tracepoint",
+                                 stopping_tracepoint);
+             if (ts->stop_reason == tracepoint_error)
+               ui_out_field_string (uiout, "error-description",
+                                    ts->error_desc);
+           }
+       }
+    }
+
+
+  if ((int) ts->traceframe_count != -1)
+    ui_out_field_int (uiout, "frames", ts->traceframe_count);
+  if ((int) ts->buffer_size != -1)
+    ui_out_field_int (uiout, "buffer-size",  (int) ts->buffer_size);
+  if ((int) ts->buffer_free != -1)
+    ui_out_field_int (uiout, "buffer-free",  (int) ts->buffer_free);
+}
+
+
 void
 disconnect_or_stop_tracing (int from_tty)
 {
@@ -1682,13 +1765,20 @@ disconnect_or_stop_tracing (int from_tty)
       if (!cont)
        stop_tracing ();
     }
+
+  /* Also we want to be out of tfind mode, otherwise things can get
+     confusing upon reconnection.  Just use these calls instead of
+     full tfind_1 behavior because we're in the middle of detaching,
+     and there's no point to updating current stack frame etc.  */
+  set_traceframe_number (-1);
+  set_traceframe_context (NULL);
 }
 
 /* Worker function for the various flavors of the tfind command.  */
-static void
-finish_tfind_command (enum trace_find_type type, int num,
-                     ULONGEST addr1, ULONGEST addr2,
-                     int from_tty)
+void
+tfind_1 (enum trace_find_type type, int num,
+        ULONGEST addr1, ULONGEST addr2,
+        int from_tty)
 {
   int target_frameno = -1, target_tracept = -1;
   struct frame_id old_frame_id;
@@ -1747,6 +1837,7 @@ finish_tfind_command (enum trace_find_type type, int num,
 
   reinit_frame_cache ();
   registers_changed ();
+  target_dcache_invalidate ();
   set_traceframe_num (target_frameno);
   set_tracepoint_num (tp ? tp->number : target_tracept);
   if (target_frameno == -1)
@@ -1754,6 +1845,30 @@ finish_tfind_command (enum trace_find_type type, int num,
   else
     set_traceframe_context (get_current_frame ());
 
+  if (traceframe_number >= 0)
+    {
+      /* Use different branches for MI and CLI to make CLI messages
+        i18n-eable.  */
+      if (ui_out_is_mi_like_p (uiout))
+       {
+         ui_out_field_string (uiout, "found", "1");
+         ui_out_field_int (uiout, "tracepoint", tracepoint_number);
+         ui_out_field_int (uiout, "traceframe", traceframe_number);
+       }
+      else
+       {
+         printf_unfiltered (_("Found trace frame %d, tracepoint %d\n"),
+                            traceframe_number, tracepoint_number);
+       }
+    }
+  else
+    {
+      if (ui_out_is_mi_like_p (uiout))
+       ui_out_field_string (uiout, "found", "0");
+      else
+       printf_unfiltered (_("No trace frame found"));
+    }
+
   /* If we're in nonstop mode and getting out of looking at trace
      frames, there won't be any current frame to go back to and
      display.  */
@@ -1827,7 +1942,7 @@ trace_find_command (char *args, int from_tty)
   if (frameno < -1)
     error (_("invalid input (%d is less than zero)"), frameno);
 
-  finish_tfind_command (tfind_number, frameno, 0, 0, from_tty);
+  tfind_1 (tfind_number, frameno, 0, 0, from_tty);
 }
 
 /* tfind end */
@@ -1866,7 +1981,7 @@ trace_find_pc_command (char *args, int from_tty)
   else
     pc = parse_and_eval_address (args);
 
-  finish_tfind_command (tfind_pc, 0, pc, 0, from_tty);
+  tfind_1 (tfind_pc, 0, pc, 0, from_tty);
 }
 
 /* tfind tracepoint command */
@@ -1896,7 +2011,7 @@ trace_find_tracepoint_command (char *args, int from_tty)
   if (tp)
     tdp = tp->number_on_target;
 
-  finish_tfind_command (tfind_tp, tdp, 0, 0, from_tty);
+  tfind_1 (tfind_tp, tdp, 0, 0, from_tty);
 }
 
 /* TFIND LINE command:
@@ -1928,33 +2043,16 @@ trace_find_line_command (char *args, int from_tty)
       sals.sals[0] = sal;
     }
   else
-      {
+    {
       sals = decode_line_spec (args, 1);
       sal = sals.sals[0];
     }
   
   old_chain = make_cleanup (xfree, sals.sals);
   if (sal.symtab == 0)
-    {
-      printf_filtered ("TFIND: No line number information available");
-      if (sal.pc != 0)
-       {
-         /* This is useful for "info line *0x7f34".  If we can't
-            tell the user about a source line, at least let them
-            have the symbolic address.  */
-         printf_filtered (" for address ");
-         wrap_here ("  ");
-         print_address (get_current_arch (), sal.pc, gdb_stdout);
-         printf_filtered (";\n -- will attempt to find by PC. \n");
-       }
-        else
-       {
-         printf_filtered (".\n");
-         return;               /* No line, no PC; what can we do?  */
-       }
-    }
-  else if (sal.line > 0
-          && find_line_pc_range (sal, &start_pc, &end_pc))
+    error (_("No line number information available."));
+
+  if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc))
     {
       if (start_pc == end_pc)
        {
@@ -1984,9 +2082,9 @@ trace_find_line_command (char *args, int from_tty)
 
   /* Find within range of stated line.  */
   if (args && *args)
-    finish_tfind_command (tfind_range, 0, start_pc, end_pc - 1, from_tty);
+    tfind_1 (tfind_range, 0, start_pc, end_pc - 1, from_tty);
   else
-    finish_tfind_command (tfind_outside, 0, start_pc, end_pc - 1, from_tty);
+    tfind_1 (tfind_outside, 0, start_pc, end_pc - 1, from_tty);
   do_cleanups (old_chain);
 }
 
@@ -2021,7 +2119,7 @@ trace_find_range_command (char *args, int from_tty)
       stop = start + 1;        /* ??? */
     }
 
-  finish_tfind_command (tfind_range, 0, start, stop, from_tty);
+  tfind_1 (tfind_range, 0, start, stop, from_tty);
 }
 
 /* tfind outside command */
@@ -2055,7 +2153,7 @@ trace_find_outside_command (char *args, int from_tty)
       stop = start + 1;        /* ??? */
     }
 
-  finish_tfind_command (tfind_outside, 0, start, stop, from_tty);
+  tfind_1 (tfind_outside, 0, start, stop, from_tty);
 }
 
 /* info scope command: list the locals for a scope.  */
@@ -2220,11 +2318,12 @@ trace_dump_command (char *args, int from_tty)
   struct regcache *regcache;
   struct gdbarch *gdbarch;
   struct breakpoint *t;
-  struct action_line *action;
+  struct command_line *action;
   char *action_exp, *next_comma;
   struct cleanup *old_cleanups;
   int stepping_actions = 0;
   int stepping_frame = 0;
+  struct bp_location *loc;
 
   if (tracepoint_number == -1)
     {
@@ -2250,14 +2349,21 @@ trace_dump_command (char *args, int from_tty)
   regcache = get_current_regcache ();
   gdbarch = get_regcache_arch (regcache);
 
-  stepping_frame = (t->loc->address != (regcache_read_pc (regcache)));
+  /* If the traceframe's address matches any of the tracepoint's
+     locations, assume it is a direct hit rather than a while-stepping
+     frame.  (FIXME this is not reliable, should record each frame's
+     type.)  */
+  stepping_frame = 1;
+  for (loc = t->loc; loc; loc = loc->next)
+    if (loc->address == regcache_read_pc (regcache))
+      stepping_frame = 0;
 
-  for (action = t->actions; action; action = action->next)
+  for (action = t->commands->commands; action; action = action->next)
     {
       struct cmd_list_element *cmd;
 
       QUIT;                    /* allow user to bail out with ^C */
-      action_exp = action->action;
+      action_exp = action->line;
       while (isspace ((int) *action_exp))
        action_exp++;
 
@@ -2323,44 +2429,48 @@ trace_dump_command (char *args, int from_tty)
   discard_cleanups (old_cleanups);
 }
 
+/* Encode a piece of a tracepoint's source-level definition in a form
+   that is suitable for both protocol and saving in files.  */
+/* This version does not do multiple encodes for long strings; it should
+   return an offset to the next piece to encode.  FIXME  */
+
+extern int
+encode_source_string (int tpnum, ULONGEST addr,
+                     char *srctype, char *src, char *buf, int buf_size)
+{
+  if (80 + strlen (srctype) > buf_size)
+    error (_("Buffer too small for source encoding"));
+  sprintf (buf, "%x:%s:%s:%x:%x:",
+          tpnum, phex_nz (addr, sizeof (addr)), srctype, 0, (int) strlen (src));
+  if (strlen (buf) + strlen (src) * 2 >= buf_size)
+    error (_("Source string too long for buffer"));
+  bin2hex (src, buf + strlen (buf), 0);
+  return -1;
+}
+
 extern int trace_regblock_size;
 
-static void
-trace_save_command (char *args, int from_tty)
+/* Save tracepoint data to file named FILENAME.  If TARGET_DOES_SAVE is
+   non-zero, the save is performed on the target, otherwise GDB obtains all
+   trace data and saves it locally.  */
+
+void
+trace_save (const char *filename, int target_does_save)
 {
-  char **argv;
-  char *filename = NULL, *pathname;
-  int target_does_save = 0;
   struct cleanup *cleanup;
+  char *pathname;
   struct trace_status *ts = current_trace_status ();
   int err, status;
   FILE *fp;
   struct uploaded_tp *uploaded_tps = NULL, *utp;
   struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
   int a;
+  struct uploaded_string *cmd;
   LONGEST gotten = 0;
   ULONGEST offset = 0;
 #define MAX_TRACE_UPLOAD 2000
   gdb_byte buf[MAX_TRACE_UPLOAD];
-
-  if (args == NULL)
-    error_no_arg (_("file in which to save trace data"));
-
-  argv = gdb_buildargv (args);
-  make_cleanup_freeargv (argv);
-
-  for (; *argv; ++argv)
-    {
-      if (strcmp (*argv, "-r") == 0)
-       target_does_save = 1;
-      else if (**argv == '-')
-       error (_("unknown option `%s'"), *argv);
-      else
-       filename = *argv;
-    }
-
-  if (!filename)
-    error_no_arg (_("file in which to save trace data"));
+  int written;
 
   /* If the target is to save the data to a file on its own, then just
      send the command and be done with it.  */
@@ -2377,19 +2487,21 @@ trace_save_command (char *args, int from_tty)
      target is losing, we can get out without touching files.  */
   status = target_get_trace_status (ts);
 
-  pathname = tilde_expand (args);
+  pathname = tilde_expand (filename);
   cleanup = make_cleanup (xfree, pathname);
 
   fp = fopen (pathname, "w");
   if (!fp)
     error (_("Unable to open file '%s' for saving trace data (%s)"),
-          args, safe_strerror (errno));
+          filename, safe_strerror (errno));
   make_cleanup_fclose (fp);
 
   /* Write a file header, with a high-bit-set char to indicate a
      binary file, plus a hint as what this file is, and a version
      number in case of future needs.  */
-  fwrite ("\x7fTRACE0\n", 8, 1, fp);
+  written = fwrite ("\x7fTRACE0\n", 8, 1, fp);
+  if (written < 1)
+    perror_with_name (pathname);
 
   /* Write descriptive info.  */
 
@@ -2397,10 +2509,24 @@ trace_save_command (char *args, int from_tty)
   fprintf (fp, "R %x\n", trace_regblock_size);
 
   /* Write out status of the tracing run (aka "tstatus" info).  */
-  fprintf (fp, "status %c;%s:%x;tframes:%x;tfree:%x\n",
-          (ts->running ? '1' : '0'),
-          stop_reason_names[ts->stop_reason], ts->stopping_tracepoint,
-          ts->traceframe_count, (unsigned int) ts->buffer_free);
+  fprintf (fp, "status %c;%s",
+          (ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
+  if (ts->stop_reason == tracepoint_error)
+    {
+      char *buf = (char *) alloca (strlen (ts->error_desc) * 2 + 1);
+      bin2hex ((gdb_byte *) ts->error_desc, buf, 0);
+      fprintf (fp, ":%s", buf);
+    }
+  fprintf (fp, ":%x", ts->stopping_tracepoint);
+  if (ts->traceframe_count >= 0)
+    fprintf (fp, ";tframes:%x", ts->traceframe_count);
+  if (ts->traceframes_created >= 0)
+    fprintf (fp, ";tcreated:%x", ts->traceframes_created);
+  if (ts->buffer_free >= 0)
+    fprintf (fp, ";tfree:%x", ts->buffer_free);
+  if (ts->buffer_size >= 0)
+    fprintf (fp, ";tsize:%x", ts->buffer_size);
+  fprintf (fp, "\n");
 
   /* Note that we want to upload tracepoints and save those, rather
      than simply writing out the local ones, because the user may have
@@ -2455,6 +2581,24 @@ trace_save_command (char *args, int from_tty)
        fprintf (fp, "tp S%x:%s:%s\n",
                 utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
                 utp->step_actions[a]);
+      if (utp->at_string)
+       {
+         encode_source_string (utp->number, utp->addr,
+                               "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+         fprintf (fp, "tp Z%s\n", buf);
+       }
+      if (utp->cond_string)
+       {
+         encode_source_string (utp->number, utp->addr,
+                               "cond", utp->cond_string, buf, MAX_TRACE_UPLOAD);
+         fprintf (fp, "tp Z%s\n", buf);
+       }
+      for (cmd = utp->cmd_strings; cmd; cmd = cmd->next)
+       {
+         encode_source_string (utp->number, utp->addr, "cmd", cmd->str,
+                               buf, MAX_TRACE_UPLOAD);
+         fprintf (fp, "tp Z%s\n", buf);
+       }
     }
 
   free_uploaded_tps (&uploaded_tps);
@@ -2473,16 +2617,53 @@ trace_save_command (char *args, int from_tty)
       /* No more data is forthcoming, we're done.  */
       if (gotten == 0)
        break;
-      fwrite (buf, gotten, 1, fp);
+      written = fwrite (buf, gotten, 1, fp);
+      if (written < 1)
+       perror_with_name (pathname);
       offset += gotten;
     }
 
-  /* Mark the end of trace data.  */
-  fwrite (&gotten, 4, 1, fp);
+  /* Mark the end of trace data.  (We know that gotten is 0 at this point.)  */
+  written = fwrite (&gotten, 4, 1, fp);
+  if (written < 1)
+    perror_with_name (pathname);
 
   do_cleanups (cleanup);
+}
+
+static void
+trace_save_command (char *args, int from_tty)
+{
+  int target_does_save = 0;
+  char **argv;
+  char *filename = NULL;
+  struct cleanup *back_to;
+
+  if (args == NULL)
+    error_no_arg (_("file in which to save trace data"));
+
+  argv = gdb_buildargv (args);
+  back_to = make_cleanup_freeargv (argv);
+
+  for (; *argv; ++argv)
+    {
+      if (strcmp (*argv, "-r") == 0)
+       target_does_save = 1;
+      else if (**argv == '-')
+       error (_("unknown option `%s'"), *argv);
+      else
+       filename = *argv;
+    }
+
+  if (!filename)
+    error_no_arg (_("file in which to save trace data"));
+
+  trace_save (filename, target_does_save);
+
   if (from_tty)
     printf_filtered (_("Trace data saved to file '%s'.\n"), args);
+
+  do_cleanups (back_to);
 }
 
 /* Tell the target what to do with an ongoing tracing run if GDB
@@ -2501,6 +2682,13 @@ set_disconnected_tracing (char *args, int from_tty,
   send_disconnected_tracing_value (disconnected_tracing);
 }
 
+static void
+set_circular_trace_buffer (char *args, int from_tty,
+                          struct cmd_list_element *c)
+{
+  target_set_circular_trace_buffer (circular_trace_buffer);
+}
+
 /* Convert the memory pointed to by mem into hex, placing result in buf.
  * Return a pointer to the last char put in buf (null)
  * "stolen" from sparc-stub.c
@@ -2532,6 +2720,67 @@ get_traceframe_number (void)
   return traceframe_number;
 }
 
+/* Make the traceframe NUM be the current trace frame.  Does nothing
+   if NUM is already current.  */
+
+void
+set_traceframe_number (int num)
+{
+  int newnum;
+
+  if (traceframe_number == num)
+    {
+      /* Nothing to do.  */
+      return;
+    }
+
+  newnum = target_trace_find (tfind_number, num, 0, 0, NULL);
+
+  if (newnum != num)
+    warning (_("could not change traceframe"));
+
+  traceframe_number = newnum;
+
+  /* Changing the traceframe changes our view of registers and of the
+     frame chain.  */
+  registers_changed ();
+}
+
+/* A cleanup used when switching away and back from tfind mode.  */
+
+struct current_traceframe_cleanup
+{
+  /* The traceframe we were inspecting.  */
+  int traceframe_number;
+};
+
+static void
+do_restore_current_traceframe_cleanup (void *arg)
+{
+  struct current_traceframe_cleanup *old = arg;
+
+  set_traceframe_number (old->traceframe_number);
+}
+
+static void
+restore_current_traceframe_cleanup_dtor (void *arg)
+{
+  struct current_traceframe_cleanup *old = arg;
+
+  xfree (old);
+}
+
+struct cleanup *
+make_cleanup_restore_current_traceframe (void)
+{
+  struct current_traceframe_cleanup *old;
+
+  old = xmalloc (sizeof (struct current_traceframe_cleanup));
+  old->traceframe_number = traceframe_number;
+
+  return make_cleanup_dtor (do_restore_current_traceframe_cleanup, old,
+                           restore_current_traceframe_cleanup_dtor);
+}
 
 /* Given a number and address, return an uploaded tracepoint with that
    number, creating if necessary.  */
@@ -2766,6 +3015,7 @@ struct target_ops tfile_ops;
 
 #define TRACE_HEADER_SIZE 8
 
+char *trace_filename;
 int trace_fd = -1;
 off_t trace_frames_offset;
 off_t cur_offset;
@@ -2786,7 +3036,7 @@ tfile_open (char *filename, int from_tty)
   char header[TRACE_HEADER_SIZE];
   char linebuf[1000]; /* should be max remote packet size or so */
   char byte;
-  int bytes, i;
+  int bytes, i, gotten;
   struct trace_status *ts;
   struct uploaded_tp *uploaded_tps = NULL;
   struct uploaded_tsv *uploaded_tsvs = NULL;
@@ -2817,13 +3067,18 @@ tfile_open (char *filename, int from_tty)
   unpush_target (&tfile_ops);
 
   push_target (&tfile_ops);
-  discard_cleanups (old_chain);
 
+  trace_filename = xstrdup (filename);
   trace_fd = scratch_chan;
 
   bytes = 0;
   /* Read the file header and test for validity.  */
-  read (trace_fd, &header, TRACE_HEADER_SIZE);
+  gotten = read (trace_fd, &header, TRACE_HEADER_SIZE);
+  if (gotten < 0)
+    perror_with_name (trace_filename);
+  else if (gotten < TRACE_HEADER_SIZE)
+    error (_("Premature end of file while reading trace file"));
+
   bytes += TRACE_HEADER_SIZE;
   if (!(header[0] == 0x7f
        && (strncmp (header + 1, "TRACE0\n", 7) == 0)))
@@ -2844,7 +3099,12 @@ tfile_open (char *filename, int from_tty)
   i = 0;
   while (1)
     {
-      read (trace_fd, &byte, 1);
+      gotten = read (trace_fd, &byte, 1);
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 1)
+       error (_("Premature end of file while reading trace file"));
+
       ++bytes;
       if (byte == '\n')
        {
@@ -2863,10 +3123,12 @@ tfile_open (char *filename, int from_tty)
 
   /* Add the file's tracepoints and variables into the current mix.  */
 
-  merge_uploaded_tracepoints (&uploaded_tps);
-
+  /* Get trace state variables first, they may be checked when parsing
+     uploaded commands.  */
   merge_uploaded_trace_state_variables (&uploaded_tsvs);
 
+  merge_uploaded_tracepoints (&uploaded_tps);
+
   /* Record the starting offset of the binary trace data.  */
   trace_frames_offset = bytes;
 
@@ -2934,12 +3196,19 @@ extern char *unpack_varlen_hex (char *buff, ULONGEST *result);
 void
 parse_trace_status (char *line, struct trace_status *ts)
 {
-  char *p = line, *p1, *p_temp;
+  char *p = line, *p1, *p2, *p_temp;
   ULONGEST val;
 
   ts->running_known = 1;
   ts->running = (*p++ == '1');
   ts->stop_reason = trace_stop_reason_unknown;
+  xfree (ts->error_desc);
+  ts->error_desc = NULL;
+  ts->traceframe_count = -1;
+  ts->traceframes_created = -1;
+  ts->buffer_free = -1;
+  ts->buffer_size = -1;
+
   while (*p++)
     {
       p1 = strchr (p, ':');
@@ -2967,16 +3236,44 @@ Status line: '%s'\n"), p, line);
          p = unpack_varlen_hex (++p1, &val);
          ts->stop_reason = tstop_command;
        }
-      if (strncmp (p, "tframes", p1 - p) == 0)
+      else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0)
+       {
+         p2 = strchr (++p1, ':');
+         if (p2 != p1)
+           {
+             int end;
+
+             ts->error_desc = xmalloc ((p2 - p1) / 2 + 1);
+             end = hex2bin (p1, ts->error_desc, (p2 - p1) / 2);
+             ts->error_desc[end] = '\0';
+           }
+         else
+           ts->error_desc = xstrdup ("");
+
+         p = unpack_varlen_hex (++p2, &val);
+         ts->stopping_tracepoint = val;
+         ts->stop_reason = tracepoint_error;
+       }
+      else if (strncmp (p, "tframes", p1 - p) == 0)
        {
          p = unpack_varlen_hex (++p1, &val);
          ts->traceframe_count = val;
        }
-      if (strncmp (p, "tfree", p1 - p) == 0)
+      else if (strncmp (p, "tcreated", p1 - p) == 0)
+       {
+         p = unpack_varlen_hex (++p1, &val);
+         ts->traceframes_created = val;
+       }
+      else if (strncmp (p, "tfree", p1 - p) == 0)
        {
          p = unpack_varlen_hex (++p1, &val);
          ts->buffer_free = val;
        }
+      else if (strncmp (p, "tsize", p1 - p) == 0)
+       {
+         p = unpack_varlen_hex (++p1, &val);
+         ts->buffer_size = val;
+       }
       else
        {
          /* Silently skip unknown optional info.  */
@@ -2990,18 +3287,18 @@ Status line: '%s'\n"), p, line);
     }
 }
 
-/* Given a line of text defining a tracepoint or tracepoint action, parse
-   it into an "uploaded tracepoint".  */
+/* Given a line of text defining a part of a tracepoint, parse it into
+   an "uploaded tracepoint".  */
 
 void
 parse_tracepoint_definition (char *line, struct uploaded_tp **utpp)
 {
   char *p;
   char piece;
-  ULONGEST num, addr, step, pass, orig_size, xlen;
-  int enabled, i;
+  ULONGEST num, addr, step, pass, orig_size, xlen, start;
+  int enabled, i, end;
   enum bptype type;
-  char *cond;
+  char *cond, *srctype, *src, *buf;
   struct uploaded_tp *utp = NULL;
 
   p = line;
@@ -3042,7 +3339,7 @@ parse_tracepoint_definition (char *line, struct uploaded_tp **utpp)
              p += 2 * xlen;
            }
          else
-           warning ("Unrecognized char '%c' in tracepoint definition, skipping rest", *p);
+           warning (_("Unrecognized char '%c' in tracepoint definition, skipping rest"), *p);
        }
       utp = get_uploaded_tp (num, addr, utpp);
       utp->type = type;
@@ -3061,9 +3358,49 @@ parse_tracepoint_definition (char *line, struct uploaded_tp **utpp)
       utp = get_uploaded_tp (num, addr, utpp);
       utp->step_actions[utp->num_step_actions++] = xstrdup (p);
     }
+  else if (piece == 'Z')
+    {
+      /* Parse a chunk of source form definition.  */
+      utp = get_uploaded_tp (num, addr, utpp);
+      srctype = p;
+      p = strchr (p, ':');
+      p++;  /* skip a colon */
+      p = unpack_varlen_hex (p, &start);
+      p++;  /* skip a colon */
+      p = unpack_varlen_hex (p, &xlen);
+      p++;  /* skip a colon */
+
+      buf = alloca (strlen (line));
+
+      end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
+      buf[end] = '\0';
+
+      if (strncmp (srctype, "at:", strlen ("at:")) == 0)
+       utp->at_string = xstrdup (buf);
+      else if (strncmp (srctype, "cond:", strlen ("cond:")) == 0)
+       utp->cond_string = xstrdup (buf);
+      else if (strncmp (srctype, "cmd:", strlen ("cmd:")) == 0)
+       {
+         /* FIXME consider using a vector? */
+         struct uploaded_string *last, *newlast;
+         newlast = (struct uploaded_string *) xmalloc (sizeof (struct uploaded_string));
+         newlast->str = xstrdup (buf);
+         newlast->next = NULL;
+         if (utp->cmd_strings)
+           {
+             for (last = utp->cmd_strings; last->next; last = last->next)
+               ;
+             last->next = newlast;
+           }
+         else
+           utp->cmd_strings = newlast;
+       }
+    }
   else
     {
-      error ("Invalid tracepoint piece");
+      /* Don't error out, the target might be sending us optional
+        info that we don't care about.  */
+      warning (_("Unrecognized tracepoint piece '%c', ignoring"), piece);
     }
 }
 
@@ -3112,6 +3449,8 @@ tfile_close (int quitting)
 
   close (trace_fd);
   trace_fd = -1;
+  if (trace_filename)
+    xfree (trace_filename);
 }
 
 static void
@@ -3144,13 +3483,20 @@ tfile_get_traceframe_address (off_t tframe_offset)
   short tpnum;
   struct breakpoint *tp;
   off_t saved_offset = cur_offset;
+  int gotten;
 
   /* FIXME dig pc out of collected registers */
 
   /* Fall back to using tracepoint address.  */
   lseek (trace_fd, tframe_offset, SEEK_SET);
-  read (trace_fd, &tpnum, 2);
+  gotten = read (trace_fd, &tpnum, 2);
+  if (gotten < 0)
+    perror_with_name (trace_filename);
+  else if (gotten < 2)
+    error (_("Premature end of file while reading trace file"));
+
   tp = get_tracepoint_by_number_on_target (tpnum);
+  /* FIXME this is a poor heuristic if multiple locations */
   if (tp && tp->loc)
     addr = tp->loc->address;
 
@@ -3170,7 +3516,7 @@ tfile_trace_find (enum trace_find_type type, int num,
                  ULONGEST addr1, ULONGEST addr2, int *tpp)
 {
   short tpnum;
-  int tfnum = 0, found = 0;
+  int tfnum = 0, found = 0, gotten;
   int data_size;
   struct breakpoint *tp;
   off_t offset, tframe_offset;
@@ -3181,11 +3527,19 @@ tfile_trace_find (enum trace_find_type type, int num,
   while (1)
     {
       tframe_offset = offset;
-      read (trace_fd, &tpnum, 2);
+      gotten = read (trace_fd, &tpnum, 2);
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 2)
+       error (_("Premature end of file while reading trace file"));
       offset += 2;
       if (tpnum == 0)
        break;
-      read (trace_fd, &data_size, 4);  
+      gotten = read (trace_fd, &data_size, 4); 
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 4)
+       error (_("Premature end of file while reading trace file"));
       offset += 4;
       switch (type)
        {
@@ -3218,7 +3572,6 @@ tfile_trace_find (enum trace_find_type type, int num,
        }
       if (found)
        {
-         printf_filtered ("Found traceframe %d.\n", tfnum);
          if (tpp)
            *tpp = tpnum;
          cur_offset = offset;
@@ -3246,7 +3599,7 @@ tfile_fetch_registers (struct target_ops *ops,
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
   char block_type;
-  int i, pos, offset, regn, regsize;
+  int i, pos, offset, regn, regsize, gotten;
   unsigned short mlen;
   char *regs;
 
@@ -3261,12 +3614,22 @@ tfile_fetch_registers (struct target_ops *ops,
   pos = 0;
   while (pos < cur_data_size)
     {
-      read (trace_fd, &block_type, 1);
+      gotten = read (trace_fd, &block_type, 1);
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 1)
+       error (_("Premature end of file while reading trace file"));
+
       ++pos;
       switch (block_type)
        {
        case 'R':
-         read (trace_fd, regs, trace_regblock_size);
+         gotten = read (trace_fd, regs, trace_regblock_size);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < trace_regblock_size)
+           error (_("Premature end of file while reading trace file"));
+
          /* Assume the block is laid out in GDB register number order,
             each register with the size that it has in GDB.  */
          offset = 0;
@@ -3293,7 +3656,11 @@ tfile_fetch_registers (struct target_ops *ops,
          return;
        case 'M':
          lseek (trace_fd, 8, SEEK_CUR);
-         read (trace_fd, &mlen, 2);
+         gotten = read (trace_fd, &mlen, 2);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < 2)
+           error (_("Premature end of file while reading trace file"));
          lseek (trace_fd, mlen, SEEK_CUR);
          pos += (8 + 2 + mlen);
          break;
@@ -3315,7 +3682,7 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
                    const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
 {
   char block_type;
-  int pos;
+  int pos, gotten;
   ULONGEST maddr;
   unsigned short mlen;
 
@@ -3330,7 +3697,11 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
   pos = 0;
   while (pos < cur_data_size)
     {
-      read (trace_fd, &block_type, 1);
+      gotten = read (trace_fd, &block_type, 1);
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 1)
+       error (_("Premature end of file while reading trace file"));
       ++pos;
       switch (block_type)
        {
@@ -3339,11 +3710,25 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
          pos += trace_regblock_size;
          break;
        case 'M':
-         read (trace_fd, &maddr, 8);
-         read (trace_fd, &mlen, 2);
+         gotten = read (trace_fd, &maddr, 8);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < 8)
+           error (_("Premature end of file while reading trace file"));
+
+         gotten = read (trace_fd, &mlen, 2);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < 2)
+           error (_("Premature end of file while reading trace file"));
          if (maddr <= offset && (offset + len) <= (maddr + mlen))
            {
-             read (trace_fd, readbuf, mlen);
+             gotten = read (trace_fd, readbuf, mlen);
+             if (gotten < 0)
+               perror_with_name (trace_filename);
+             else if (gotten < mlen)
+               error (_("Premature end of file qwhile reading trace file"));
+
              return mlen;
            }
          lseek (trace_fd, mlen, SEEK_CUR);
@@ -3370,14 +3755,18 @@ static int
 tfile_get_trace_state_variable_value (int tsvnum, LONGEST *val)
 {
   char block_type;
-  int pos, vnum;
+  int pos, vnum, gotten;
   unsigned short mlen;
 
   lseek (trace_fd, cur_offset, SEEK_SET);
   pos = 0;
   while (pos < cur_data_size)
     {
-      read (trace_fd, &block_type, 1);
+      gotten = read (trace_fd, &block_type, 1);
+      if (gotten < 0)
+       perror_with_name (trace_filename);
+      else if (gotten < 1)
+       error (_("Premature end of file while reading trace file"));
       ++pos;
       switch (block_type)
        {
@@ -3387,15 +3776,27 @@ tfile_get_trace_state_variable_value (int tsvnum, LONGEST *val)
          break;
        case 'M':
          lseek (trace_fd, 8, SEEK_CUR);
-         read (trace_fd, &mlen, 2);
+         gotten = read (trace_fd, &mlen, 2);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < 2)
+           error (_("Premature end of file while reading trace file"));
          lseek (trace_fd, mlen, SEEK_CUR);
          pos += (8 + 2 + mlen);
          break;
        case 'V':
-         read (trace_fd, &vnum, 4);
+         gotten = read (trace_fd, &vnum, 4);
+         if (gotten < 0)
+           perror_with_name (trace_filename);
+         else if (gotten < 4)
+           error (_("Premature end of file while reading trace file"));
          if (tsvnum == vnum)
            {
-             read (trace_fd, val, 8);
+             gotten = read (trace_fd, val, 8);
+             if (gotten < 0)
+               perror_with_name (trace_filename);
+             else if (gotten < 8)
+               error (_("Premature end of file while reading trace file"));
              return 1;
            }
          lseek (trace_fd, 8, SEEK_CUR);
@@ -3527,12 +3928,12 @@ No argument means forward by one frame; '-' means backward by one frame."),
                  &tfindlist, "tfind ", 1, &cmdlist);
 
   add_cmd ("outside", class_trace, trace_find_outside_command, _("\
-Select a trace frame whose PC is outside the given range.\n\
+Select a trace frame whose PC is outside the given range (exclusive).\n\
 Usage: tfind outside addr1, addr2"),
           &tfindlist);
 
   add_cmd ("range", class_trace, trace_find_range_command, _("\
-Select a trace frame whose PC is in the given range.\n\
+Select a trace frame whose PC is in the given range (inclusive).\n\
 Usage: tfind range addr1,addr2"),
           &tfindlist);
 
@@ -3635,6 +4036,18 @@ trace data collected in the meantime."),
                           &setlist,
                           &showlist);
 
+  add_setshow_boolean_cmd ("circular-trace-buffer", no_class,
+                          &circular_trace_buffer, _("\
+Set target's use of circular trace buffer."), _("\
+Show target's use of circular trace buffer."), _("\
+Use this to make the trace buffer into a circular buffer,\n\
+which will discard traceframes (oldest first) instead of filling\n\
+up and stopping the trace run."),
+                          set_circular_trace_buffer,
+                          NULL,
+                          &setlist,
+                          &showlist);
+
   init_tfile_ops ();
 
   add_target (&tfile_ops);
This page took 0.047481 seconds and 4 git commands to generate.