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] Trace file support


This patch adds support for trace files, which are simply dumps of a target's tracepoints and trace buffer. In addition to the new "tsave" command to create a trace file, there is a new target "tfile" that opens a trace file and then lets you do tfind and then print any data that was collected, just as for you would do for a live target.

I apologize in advance for the size of this patch; while about half of it is simply the new code that does all the file reading and writing, the other half is needed to rework the uploading machinery that was introduced for disconnected tracing, so we can do the same kind of parsing for tracepoint definitions, whether they come from a file or a live target. This patch also includes an expansion of tstatus, again with shared parsing between file and remote, that can display useful details like trace buffer usage and the reason for a trace run having stopped. Despite the size, the changes are all confined to tracing.

Originally I thought this was going to be untestable until we got gdbserver patches in - while in theory one could make up some generic trace files and add them to testsuite, it's hard to make all the addresses and such line up correctly and in an arch-independent way. But it's occurred to me that a test program could itself manually construct a trace file, simply taking the addresses of its own variables to set up useful memory blocks, and so I'm giving that a try. In the meantime please give this patch a once-over, point out anything that seems missing.

Stan

2010-01-11 Stan Shebs <stan@codesourcery.com>

   Add trace file support.
   * tracepoint.h (enum trace_stop_reason): New enum.
   (struct trace_status): New struct.
   (parse_trace_status): Declare.
   (struct uploaded_tp): Move here from remote.c,
   add fields for actions.
   (struct uploaded_tsv): New struct.
   * tracepoint.c (tfile_ops): New target vector.
   (trace_fd): New global.
   (tfile_open): New function.
   (tfile_close): New function.
   (tfile_files_info): New function.
   (tfile_get_trace_status): New function.
   (tfile_get_traceframe_address): New function.
   (tfile_trace_find): New function.
   (tfile_fetch_registers): New function.
   (tfile_xfer_partial): New function.
   (tfile_get_trace_state_variable_value): New function.
   (init_tfile_ops): New function.
   (_initialize_tracepoint): Call it, add tfile target.
   (trace_status): New global.
   (current_trace_status): New function.
   (trace_running_p): Remove, change all users to get from
   current_trace_status()->running.
   (get_trace_status): Remove.
   (trace_status_command): Call target_get_trace_status directly,
   report more detail including tracing stop reasons.
   (trace_find_command): Always allow tfind on a file.
   (trace_find_pc_command): Ditto.
   (trace_find_tracepoint_command): Ditto.
   (trace_find_line_command): Ditto.
   (trace_find_range_command): Ditto.
   (trace_find_outside_command): Ditto.
   (trace_frames_offset, cur_offset): Declare as off_t.
   (trace_regblock_size): Rename from reg_size, update users.
   (parse_trace_status): New function.
   (tfile_interp_line): New function.
   (disconnect_or_stop_tracing): Ensure current trace
   status before asking what to do.
   (stop_reason_names): New global.
   (trace_save_command): New command.
   (get_uploaded_tp): Move here from remote.c.
   (find_matching_tracepoint): Ditto.
   (merge_uploaded_tracepoints): New function.
   (parse_trace_status): Use stop_reason_names.
   (_initialize_tracepoint): Define tsave command.
   * target.h (target_ops): New fields to_save_trace_data,
   to_upload_tracepoints, to_upload_trace_state_variables,
   to_get_raw_trace_data, change to_get_trace_status
   to take a pointer to a status struct.
   (target_save_trace_data): New macro.
   (target_upload_tracepoints): New macro.
   (target_upload_trace_state_variables): New macro.
   (target_get_raw_trace_data): New macro.
   * target.c (update_current_target): Add new methods, change
   signature of to_get_trace_status.
   * remote.c (hex2bin): Make globally visible.
   (bin2hex): Ditto.
   (remote_download_trace_state_variable): Download name also.
   (remote_get_trace_status): Update parameter, use
   parse_trace_status.
   (remote_save_trace_data): New function.
   (remote_upload_tracepoints): New function.
   (remote_upload_trace_state_variables): New function.
   (remote_get_raw_trace_data): New function.
   (remote_start_remote): Use them.
   (_initialize_remote_ops): Add operations.
   * ax-gdb.c: Include breakpoint.h.
   * breakpoint.c (create_tracepoint_from_upload): Use
   break_command_really, return tracepoint, warn about unimplemented
   parts.

   * gdb.texinfo (Trace Files): New section.
   (Tracepoint Packets): Document QTSave and qTBuffer.

Index: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.60
diff -p -r1.60 ax-gdb.c
*** ax-gdb.c	1 Jan 2010 07:31:30 -0000	1.60
--- ax-gdb.c	12 Jan 2010 01:42:07 -0000
***************
*** 37,42 ****
--- 37,43 ----
  #include "user-regs.h"
  #include "language.h"
  #include "dictionary.h"
+ #include "breakpoint.h"
  #include "tracepoint.h"
  
  /* To make sense of this file, you should read doc/agentexpr.texi.
Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.448
diff -p -r1.448 breakpoint.c
*** breakpoint.c	6 Jan 2010 20:31:28 -0000	1.448
--- breakpoint.c	12 Jan 2010 01:42:07 -0000
*************** ftrace_command (char *arg, int from_tty)
*** 9847,9873 ****
    set_tracepoint_count (breakpoint_count);
  }
  
! extern void create_tracepoint_from_upload (int num, enum bptype type,
! 					   ULONGEST addr);
! 
! void
! create_tracepoint_from_upload (int num, enum bptype type, ULONGEST addr)
  {
    char buf[100];
    struct breakpoint *tp;
  
!   sprintf (buf, "*0x%s", paddress (get_current_arch (), addr));
!   if (type == bp_fast_tracepoint)
!     ftrace_command (buf, 0);
!   else
!     trace_command (buf, 0);
  
!   /* Record that this tracepoint is numbered differently on host and
!      target.  */
!   tp = get_tracepoint (tracepoint_count);
!   tp->number_on_target = num;
! }
  
  /* Print information on tracepoint number TPNUM_EXP, or all if
     omitted.  */
  
--- 9847,9907 ----
    set_tracepoint_count (breakpoint_count);
  }
  
! /* Given information about a tracepoint as recorded on a target (which
!    can be either a live system or a trace file), attempt to create an
!    equivalent GDB tracepoint.  This is not a reliable process, since
!    the target does not necessarily have all the information used when
!    the tracepoint was originally defined.  */
!   
! struct breakpoint *
! create_tracepoint_from_upload (struct uploaded_tp *utp)
  {
    char buf[100];
    struct breakpoint *tp;
+   
+   /* In the absence of a source location, fall back to raw address.  */
+   sprintf (buf, "*%s", paddress (get_current_arch(), utp->addr));
  
!   break_command_really (get_current_arch (),
! 			buf, 
! 			NULL, 0, 1 /* parse arg */,
! 			0 /* tempflag */,
! 			(utp->type == bp_fast_tracepoint) /* hardwareflag */,
! 			1 /* traceflag */,
! 			0 /* Ignore count */,
! 			pending_break_support, 
! 			NULL,
! 			0 /* from_tty */,
! 			utp->enabled /* enabled */);
!   set_tracepoint_count (breakpoint_count);
!   
!     tp = get_tracepoint (tracepoint_count);
  
!   if (utp->pass > 0)
!     {
!       sprintf (buf, "%d %d", utp->pass, tp->number);
! 
!       trace_pass_command (buf, 0);
!     }
  
+   if (utp->cond)
+     {
+       printf_filtered ("Want to restore a condition\n");
+     }
+ 
+   if (utp->numactions > 0)
+     {
+       printf_filtered ("Want to restore action list\n");
+     }
+ 
+   if (utp->num_step_actions > 0)
+     {
+       printf_filtered ("Want to restore action list\n");
+     }
+ 
+   return tp;
+   }
+   
  /* Print information on tracepoint number TPNUM_EXP, or all if
     omitted.  */
  
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.379
diff -p -r1.379 remote.c
*** remote.c	7 Jan 2010 19:17:46 -0000	1.379
--- remote.c	12 Jan 2010 01:42:08 -0000
*************** static void record_currthread (ptid_t cu
*** 188,196 ****
  
  static int fromhex (int a);
  
! static int hex2bin (const char *hex, gdb_byte *bin, int count);
  
! static int bin2hex (const gdb_byte *bin, char *hex, int count);
  
  static int putpkt_binary (char *buf, int cnt);
  
--- 188,196 ----
  
  static int fromhex (int a);
  
! extern int hex2bin (const char *hex, gdb_byte *bin, int count);
  
! extern int bin2hex (const gdb_byte *bin, char *hex, int count);
  
  static int putpkt_binary (char *buf, int cnt);
  
*************** static char *write_ptid (char *buf, cons
*** 214,221 ****
  static ptid_t read_ptid (char *buf, char **obuf);
  
  struct remote_state;
! static void remote_get_tracing_state (struct remote_state *);
  
  static void remote_query_supported (void);
  
  static void remote_check_symbols (struct objfile *objfile);
--- 214,225 ----
  static ptid_t read_ptid (char *buf, char **obuf);
  
  struct remote_state;
! static int remote_get_trace_status (struct trace_status *ts);
  
+ static int remote_upload_tracepoints (struct uploaded_tp **utpp);
+ 
+ static int remote_upload_trace_state_variables (struct uploaded_tsv **utsvp);
+   
  static void remote_query_supported (void);
  
  static void remote_check_symbols (struct objfile *objfile);
*************** remote_start_remote (struct ui_out *uiou
*** 2990,2996 ****
       previously; find out where things are at.  */
    if (rs->disconnected_tracing)
      {
!       remote_get_tracing_state (rs);
      }
  
    /* If breakpoints are global, insert them now.  */
--- 2994,3016 ----
       previously; find out where things are at.  */
    if (rs->disconnected_tracing)
      {
!       struct uploaded_tp *uploaded_tps = NULL;
!       struct uploaded_tsv *uploaded_tsvs = NULL;
! 
!       remote_get_trace_status (current_trace_status ());
!       if (current_trace_status ()->running)
! 	printf_filtered (_("Trace is already running on the target.\n"));
! 
!       /* Get trace state variables first, they may be checked when
! 	 parsing uploaded commands.  */
! 
!       remote_upload_trace_state_variables (&uploaded_tsvs);
! 
!       merge_uploaded_trace_state_variables (&uploaded_tsvs);
! 
!       remote_upload_tracepoints (&uploaded_tps);
! 
!       merge_uploaded_tracepoints (&uploaded_tps);
      }
  
    /* If breakpoints are global, insert them now.  */
*************** fromhex (int a)
*** 3778,3784 ****
      error (_("Reply contains invalid hex digit %d"), a);
  }
  
! static int
  hex2bin (const char *hex, gdb_byte *bin, int count)
  {
    int i;
--- 3798,3804 ----
      error (_("Reply contains invalid hex digit %d"), a);
  }
  
! int
  hex2bin (const char *hex, gdb_byte *bin, int count)
  {
    int i;
*************** tohex (int nib)
*** 3808,3814 ****
      return 'a' + nib - 10;
  }
  
! static int
  bin2hex (const gdb_byte *bin, char *hex, int count)
  {
    int i;
--- 3828,3834 ----
      return 'a' + nib - 10;
  }
  
! int
  bin2hex (const gdb_byte *bin, char *hex, int count)
  {
    int i;
*************** static void
*** 9155,9163 ****
  remote_download_trace_state_variable (struct trace_state_variable *tsv)
  {
    struct remote_state *rs = get_remote_state ();
  
!   sprintf (rs->buf, "QTDV:%x:%s",
! 	   tsv->number, phex ((ULONGEST) tsv->initial_value, 8));
    putpkt (rs->buf);
    remote_get_noisy_reply (&target_buf, &target_buf_size);
  }
--- 9175,9189 ----
  remote_download_trace_state_variable (struct trace_state_variable *tsv)
  {
    struct remote_state *rs = get_remote_state ();
+   char *p;
  
!   sprintf (rs->buf, "QTDV:%x:%s:%x:",
! 	   tsv->number, phex ((ULONGEST) tsv->initial_value, 8), tsv->builtin);
!   p = rs->buf + strlen (rs->buf);
!   if ((p - rs->buf) + strlen (tsv->name) * 2 >= get_remote_packet_size ())
!     error (_("Trace state variable name too long for tsv definition packet"));
!   p += 2 * bin2hex ((gdb_byte *) (tsv->name), p, 0);
!   *p++ = '\0';
    putpkt (rs->buf);
    remote_get_noisy_reply (&target_buf, &target_buf_size);
  }
*************** remote_trace_start ()
*** 9208,9223 ****
  }
  
  static int
! remote_get_trace_status (int *stop_reason)
  {
    putpkt ("qTStatus");
    remote_get_noisy_reply (&target_buf, &target_buf_size);
  
!   if (target_buf[0] != 'T' ||
!       (target_buf[1] != '0' && target_buf[1] != '1'))
      error (_("Bogus trace status reply from target: %s"), target_buf);
  
!   return (target_buf[1] == '1');
  }
  
  static void
--- 9234,9271 ----
  }
  
  static int
! remote_get_trace_status (struct trace_status *ts)
  {
+   char *p, *p1, *p_temp;
+   ULONGEST val;
+   /* FIXME we need to get register block size some other way */
+   extern int trace_regblock_size;
+   trace_regblock_size = get_remote_arch_state ()->sizeof_g_packet;
+ 
    putpkt ("qTStatus");
    remote_get_noisy_reply (&target_buf, &target_buf_size);
  
!   p = target_buf;
! 
!   /* If the remote target doesn't do tracing, flag it.  */
!   if (*p == '\0')
!     return -1;
! 
!   /* We're working with a live target.  */
!   ts->from_file = 0;
! 
!   /* Set some defaults.  */
!   ts->running_known = 0;
!   ts->stop_reason = trace_stop_reason_unknown;
!   ts->traceframe_count = -1;
!   ts->buffer_free = 0;
! 
!   if (*p++ != 'T')
      error (_("Bogus trace status reply from target: %s"), target_buf);
  
!   parse_trace_status (p, ts);
! 
!   return ts->running;
  }
  
  static void
*************** remote_trace_find (enum trace_find_type 
*** 9247,9262 ****
        sprintf (p, "%x", num);
        break;
      case tfind_pc:
!       sprintf (p, "pc:%s", paddress (target_gdbarch, addr1));
        break;
      case tfind_tp:
        sprintf (p, "tdp:%x", num);
        break;
      case tfind_range:
!       sprintf (p, "range:%s:%s", paddress (target_gdbarch, addr1), paddress (target_gdbarch, addr2));
        break;
      case tfind_outside:
!       sprintf (p, "outside:%s:%s", paddress (target_gdbarch, addr1), paddress (target_gdbarch, addr2));
        break;
      default:
        error ("Unknown trace find type %d", type);
--- 9295,9310 ----
        sprintf (p, "%x", num);
        break;
      case tfind_pc:
!       sprintf (p, "pc:%s", phex_nz (addr1, 0));
        break;
      case tfind_tp:
        sprintf (p, "tdp:%x", num);
        break;
      case tfind_range:
!       sprintf (p, "range:%s:%s", phex_nz (addr1, 0), phex_nz (addr2, 0));
        break;
      case tfind_outside:
!       sprintf (p, "outside:%s:%s", phex_nz (addr1, 0), phex_nz (addr2, 0));
        break;
      default:
        error ("Unknown trace find type %d", type);
*************** remote_get_trace_state_variable_value (i
*** 9312,9317 ****
--- 9360,9426 ----
    return 0;
  }
  
+ static int
+ remote_save_trace_data (char *filename)
+ {
+   struct remote_state *rs = get_remote_state ();
+   char *p, *reply;
+ 
+   p = rs->buf;
+   strcpy (p, "QTSave:");
+   p += strlen (p);
+   if ((p - rs->buf) + strlen (filename) * 2 >= get_remote_packet_size ())
+     error (_("Remote file name too long for trace save packet"));
+   p += 2 * bin2hex ((gdb_byte *) filename, p, 0);
+   *p++ = '\0';
+   putpkt (rs->buf);
+   remote_get_noisy_reply (&target_buf, &target_buf_size);
+   return 0;
+ }
+ 
+ /* This is basically a memory transfer, but needs to be its own packet
+    because we don't know how the target actually organizes its trace
+    memory, plus we want to be able to ask for as much as possible, but
+    not be unhappy if we don't get as much as we ask for.  */
+ 
+ static LONGEST
+ remote_get_raw_trace_data (gdb_byte *buf, ULONGEST offset, LONGEST len)
+ {
+   struct remote_state *rs = get_remote_state ();
+   char *reply;
+   char *p;
+   int rslt;
+ 
+   p = rs->buf;
+   strcpy (p, "qTBuffer:");
+   p += strlen (p);
+   p += hexnumstr (p, offset);
+   *p++ = ',';
+   p += hexnumstr (p, len);
+   *p++ = '\0';
+ 
+   putpkt (rs->buf);
+   reply = remote_get_noisy_reply (&target_buf, &target_buf_size);
+   if (reply && *reply)
+     {
+       /* 'l' by itself means we're at the end of the buffer and
+ 	 there is nothing more to get.  */
+       if (*reply == 'l')
+ 	return 0;
+ 
+       /* Convert the reply into binary.  Limit the number of bytes to
+ 	 convert according to our passed-in buffer size, rather than
+ 	 what was returned in the packet; if the target is
+ 	 unexpectedly generous and gives us a bigger reply than we
+ 	 asked for, we don't want to crash.  */
+       rslt = hex2bin (target_buf, buf, len);
+       return rslt;
+     }
+ 
+   /* Something went wrong, flag as an error.  */
+   return -1;
+ }
+ 
  static void
  remote_set_disconnected_tracing (int val)
  {
*************** Specify the serial device it is connecte
*** 9396,9401 ****
--- 9505,9514 ----
    remote_ops.to_trace_stop = remote_trace_stop;
    remote_ops.to_trace_find = remote_trace_find;
    remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
+   remote_ops.to_save_trace_data = remote_save_trace_data;
+   remote_ops.to_upload_tracepoints = remote_upload_tracepoints;
+   remote_ops.to_upload_trace_state_variables = remote_upload_trace_state_variables;
+   remote_ops.to_get_raw_trace_data = remote_get_raw_trace_data;
    remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
  }
  
*************** remote_new_objfile (struct objfile *objf
*** 9547,9727 ****
      remote_check_symbols (objfile);
  }
  
! /* Struct to collect random info about tracepoints on the target.  */
! 
! struct uploaded_tp {
!   int number;
!   enum bptype type;
!   ULONGEST addr;
!   int enabled;
!   int step;
!   int pass;
!   int orig_size;
!   char *cond;
!   int cond_len;
!   struct uploaded_tp *next;
! };
! 
! struct uploaded_tp *uploaded_tps;
! 
! struct uploaded_tp *
! get_uploaded_tp (int num)
! {
!   struct uploaded_tp *utp;
! 
!   for (utp = uploaded_tps; utp; utp = utp->next)
!     if (utp->number == num)
!       return utp;
!   utp = (struct uploaded_tp *) xmalloc (sizeof (struct uploaded_tp));
!   utp->number = num;
!   utp->next = uploaded_tps;
!   uploaded_tps = utp;
!   return utp;
! }
! 
! /* Look for an existing tracepoint that seems similar enough to the
!    uploaded one.  Enablement isn't checked, because the user can
!    toggle that freely, and may have done so in anticipation of the
!    next trace run.  */
! 
! struct breakpoint *
! find_matching_tracepoint (struct uploaded_tp *utp)
  {
!   VEC(breakpoint_p) *tp_vec = all_tracepoints ();
!   int ix;
!   struct breakpoint *t;
  
!   for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
      {
!       if (t->type == utp->type
! 	  && (t->loc && t->loc->address == utp->addr)
! 	  && t->step_count == utp->step
! 	  && t->pass_count == utp->pass
! 	  /* FIXME also test conditionals and actions */
! 	  )
! 	return t;
      }
!   return NULL;
  }
  
! /* Find out everything we can about the trace run that was already
!    happening on the target.  This includes both running/stopped, and
!    the tracepoints that were in use.  */
! 
! static void
! remote_get_tracing_state (struct remote_state *rs)
  {
    char *p;
-   ULONGEST num, addr, step, pass, orig_size, xlen;
-   int enabled, i;
-   enum bptype type;
-   char *cond;
-   struct uploaded_tp *utp;
-   struct breakpoint *t;
-   extern void get_trace_status ();
- 
-   get_trace_status ();
-   if (trace_running_p)
-     printf_filtered (_("Trace is running on the target.\n"));
  
!   putpkt ("qTfP");
    getpkt (&rs->buf, &rs->buf_size, 0);
    p = rs->buf;
!   while (*p != '\0')
      {
!       if (*p == 'T')
! 	{
! 	  p++;
! 	  p = unpack_varlen_hex (p, &num);
! 	  p++;
! 	  p = unpack_varlen_hex (p, &addr);
! 	  p++;
! 	  enabled = (*p++ == 'E');
! 	  p++;
! 	  p = unpack_varlen_hex (p, &step);
! 	  p++;
! 	  p = unpack_varlen_hex (p, &pass);
! 	  p++;
! 	  type = bp_tracepoint;
! 	  cond = NULL;
! 	  while (*p)
! 	    {
! 	      if (*p == 'F')
! 		{
! 		  type = bp_fast_tracepoint;
! 		  p++;
! 		  p = unpack_varlen_hex (p, &orig_size);
! 		}
! 	      else if (*p == 'X')
! 		{
! 		  p++;
! 		  p = unpack_varlen_hex (p, &xlen);
! 		  p++;  /* skip the comma */
! 		  cond = (char *) xmalloc (xlen);
! 		  hex2bin (p, cond, xlen);
! 		  p += 2 * xlen;
! 		}
! 	      else
! 		/* Silently skip over anything else.  */
! 		p++;
! 	    }
! 	  utp = get_uploaded_tp (num);
! 	  utp->type = type;
! 	  utp->addr = addr;
! 	  utp->enabled = enabled;
! 	  utp->step = step;
! 	  utp->pass = pass;
! 	  utp->cond = cond;
! 	  utp->cond_len = xlen;
! 	}
!       else if (*p == 'A')
! 	{
! 	  p++;
! 	  p = unpack_varlen_hex (p, &num);
! 	  p++;
! 	  p = unpack_varlen_hex (p, &addr);
! 	  p++;
! 	  utp = get_uploaded_tp (num);
! 	  /* FIXME save the action */
! 	}
!       else if (*p == 'S')
! 	{
! 	  p++;
! 	  p = unpack_varlen_hex (p, &num);
! 	  p++;
! 	  p = unpack_varlen_hex (p, &addr);
! 	  p++;
! 	  utp = get_uploaded_tp (num);
! 	  /* FIXME save the action */
! 	}
!       else if (*p == 'l')
! 	{
! 	  /* No more tracepoint info, get out of the loop.  */
! 	  break;
! 	}
!       putpkt ("qTsP");
        getpkt (&rs->buf, &rs->buf_size, 0);
        p = rs->buf;
      }
!   /* Got all the tracepoint info, now look for matches among what we
!      already have in GDB.  */
!   for (utp = uploaded_tps; utp; utp = utp->next)
!     {
!       t = find_matching_tracepoint (utp);
!       if (t)
! 	{
! 	  printf_filtered (_("Assuming tracepoint %d is same as target's tracepoint %d.\n"),
! 			   t->number, utp->number);
! 	  t->number_on_target = utp->number;
! 	}
!       else
! 	{
! 	  extern void create_tracepoint_from_upload (int num, ULONGEST addr);
! 	  create_tracepoint_from_upload (utp->number, utp->addr);
! 	}
!     }
!   /* FIXME free all the space */
!   uploaded_tps = NULL;
  }
  
  void
--- 9660,9710 ----
      remote_check_symbols (objfile);
  }
  
! /* Pull all the tracepoints defined on the target and create local
!    data structures representing them.  We don't want to create real
!    tracepoints yet, we don't want to mess up the user's existing
!    collection.  */
!   
! static int
! remote_upload_tracepoints (struct uploaded_tp **utpp)
  {
!   struct remote_state *rs = get_remote_state ();
!   char *p;
  
!   /* Ask for a first packet of tracepoint definition.  */
!   putpkt ("qTfP");
!   getpkt (&rs->buf, &rs->buf_size, 0);
!   p = rs->buf;
!   while (*p && *p != 'l')
      {
!       parse_tracepoint_definition (p, utpp);
!       /* Ask for another packet of tracepoint definition.  */
!       putpkt ("qTsP");
!       getpkt (&rs->buf, &rs->buf_size, 0);
!       p = rs->buf;
      }
!   return 0;
  }
  
! static int
! remote_upload_trace_state_variables (struct uploaded_tsv **utsvp)
  {
+   struct remote_state *rs = get_remote_state ();
    char *p;
  
!   /* Ask for a first packet of variable definition.  */
!   putpkt ("qTfV");
    getpkt (&rs->buf, &rs->buf_size, 0);
    p = rs->buf;
!   while (*p && *p != 'l')
      {
!       parse_tsv_definition (p, utsvp);
!       /* Ask for another packet of variable definition.  */
!       putpkt ("qTsV");
        getpkt (&rs->buf, &rs->buf_size, 0);
        p = rs->buf;
      }
!   return 0;
  }
  
  void
Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.236
diff -p -r1.236 target.c
*** target.c	7 Jan 2010 19:17:46 -0000	1.236
--- target.c	12 Jan 2010 01:42:08 -0000
*************** update_current_target (void)
*** 694,699 ****
--- 694,703 ----
        INHERIT (to_trace_stop, t);
        INHERIT (to_trace_find, t);
        INHERIT (to_get_trace_state_variable_value, t);
+       INHERIT (to_save_trace_data, t);
+       INHERIT (to_upload_tracepoints, t);
+       INHERIT (to_upload_trace_state_variables, t);
+       INHERIT (to_get_raw_trace_data, t);
        INHERIT (to_set_disconnected_tracing, t);
        INHERIT (to_magic, t);
        /* Do not inherit to_memory_map.  */
*************** update_current_target (void)
*** 859,865 ****
  	    (void (*) (void))
  	    tcomplain);
    de_fault (to_get_trace_status,
! 	    (int (*) (int *))
  	    return_minus_one);
    de_fault (to_trace_stop,
  	    (void (*) (void))
--- 863,869 ----
  	    (void (*) (void))
  	    tcomplain);
    de_fault (to_get_trace_status,
! 	    (int (*) (struct trace_status *))
  	    return_minus_one);
    de_fault (to_trace_stop,
  	    (void (*) (void))
*************** update_current_target (void)
*** 870,875 ****
--- 874,891 ----
    de_fault (to_get_trace_state_variable_value,
  	    (int (*) (int, LONGEST *))
  	    return_zero);
+   de_fault (to_save_trace_data,
+ 	    (int (*) (char *))
+ 	    tcomplain);
+   de_fault (to_upload_tracepoints,
+ 	    (int (*) (struct uploaded_tp **))
+ 	    return_zero);
+   de_fault (to_upload_trace_state_variables,
+ 	    (int (*) (struct uploaded_tsv **))
+ 	    return_zero);
+   de_fault (to_get_raw_trace_data,
+ 	    (LONGEST (*) (gdb_byte *, ULONGEST, LONGEST))
+ 	    tcomplain);
    de_fault (to_set_disconnected_tracing,
  	    (void (*) (int))
  	    tcomplain);
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.170
diff -p -r1.170 target.h
*** target.h	7 Jan 2010 19:17:46 -0000	1.170
--- target.h	12 Jan 2010 01:42:08 -0000
*************** struct bp_target_info;
*** 32,37 ****
--- 32,40 ----
  struct regcache;
  struct target_section_table;
  struct trace_state_variable;
+ struct trace_status;
+ struct uploaded_tsv;
+ struct uploaded_tp;
  
  /* This include file defines the interface between the main part
     of the debugger, and the part which is target-specific, or
*************** struct target_ops
*** 630,636 ****
      void (*to_trace_start) (void);
  
      /* Get the current status of a tracing run.  */
!     int (*to_get_trace_status) (int *stop_reason);
  
      /* Stop a trace run.  */
      void (*to_trace_stop) (void);
--- 633,639 ----
      void (*to_trace_start) (void);
  
      /* Get the current status of a tracing run.  */
!     int (*to_get_trace_status) (struct trace_status *ts);
  
      /* Stop a trace run.  */
      void (*to_trace_stop) (void);
*************** struct target_ops
*** 647,652 ****
--- 650,664 ----
         location pointed to by VAL, else returning 0.  */
      int (*to_get_trace_state_variable_value) (int tsv, LONGEST *val);
  
+     int (*to_save_trace_data) (char *filename);
+ 
+     int (*to_upload_tracepoints) (struct uploaded_tp **utpp);
+ 
+     int (*to_upload_trace_state_variables) (struct uploaded_tsv **utsvp);
+ 
+     LONGEST (*to_get_raw_trace_data) (gdb_byte *buf,
+ 				      ULONGEST offset, LONGEST len);
+ 
      /* Set the target's tracing behavior in response to unexpected
         disconnection - set VAL to 1 to keep tracing, 0 to stop.  */
      void (*to_set_disconnected_tracing) (int val);
*************** extern int target_search_memory (CORE_AD
*** 1309,1316 ****
  #define target_trace_set_readonly_regions() \
    (*current_target.to_trace_set_readonly_regions) ()
  
! #define target_get_trace_status(stop_reason) \
!   (*current_target.to_get_trace_status) (stop_reason)
  
  #define target_trace_stop() \
    (*current_target.to_trace_stop) ()
--- 1321,1328 ----
  #define target_trace_set_readonly_regions() \
    (*current_target.to_trace_set_readonly_regions) ()
  
! #define target_get_trace_status(ts) \
!   (*current_target.to_get_trace_status) (ts)
  
  #define target_trace_stop() \
    (*current_target.to_trace_stop) ()
*************** extern int target_search_memory (CORE_AD
*** 1321,1326 ****
--- 1333,1350 ----
  #define target_get_trace_state_variable_value(tsv,val) \
    (*current_target.to_get_trace_state_variable_value) ((tsv), (val))
  
+ #define target_save_trace_data(filename) \
+   (*current_target.to_save_trace_data) (filename)
+ 
+ #define target_upload_tracepoints(utpp) \
+   (*current_target.to_upload_tracepoints) (utpp)
+ 
+ #define target_upload_trace_state_variables(utsvp) \
+   (*current_target.to_upload_trace_state_variables) (utsvp)
+ 
+ #define target_get_raw_trace_data(buf,offset,len) \
+   (*current_target.to_get_raw_trace_data) ((buf), (offset), (len))
+ 
  #define target_set_disconnected_tracing(val) \
    (*current_target.to_set_disconnected_tracing) (val)
  
Index: tracepoint.c
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.c,v
retrieving revision 1.138
diff -p -r1.138 tracepoint.c
*** tracepoint.c	7 Jan 2010 19:17:46 -0000	1.138
--- tracepoint.c	12 Jan 2010 01:42:08 -0000
***************
*** 43,48 ****
--- 43,49 ----
  #include "gdbcore.h"
  #include "objfiles.h"
  #include "filenames.h"
+ #include "gdbthread.h"
  
  #include "ax.h"
  #include "ax-gdb.h"
***************
*** 58,63 ****
--- 59,71 ----
  #include <unistd.h>
  #endif
  
+ #ifndef O_LARGEFILE
+ #define O_LARGEFILE 0
+ #endif
+ 
+ extern int hex2bin (const char *hex, gdb_byte *bin, int count);
+ extern int bin2hex (const gdb_byte *bin, char *hex, int count);
+ 
  extern void stop_tracing ();
  
  /* Maximum length of an agent aexpression.
*************** static struct cleanup *make_cleanup_free
*** 170,177 ****
--- 178,206 ----
  
  extern void send_disconnected_tracing_value (int value);
  
+ static void free_uploaded_tps (struct uploaded_tp **utpp);
+ static void free_uploaded_tsvs (struct uploaded_tsv **utsvp);
+ 
+ 
  extern void _initialize_tracepoint (void);
  
+ static struct trace_status trace_status;
+ 
+ char *stop_reason_names[] = {
+   "tunknown",
+   "tnotrun",
+   "tstop",
+   "tfull",
+   "tdisconnected",
+   "tpasscount"
+ };
+ 
+ struct trace_status *
+ current_trace_status ()
+ {
+   return &trace_status;
+ }
+ 
  /* Set traceframe number to NUM.  */
  static void
  set_traceframe_num (int num)
*************** tvariables_info (char *args, int from_tt
*** 405,411 ****
        print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout);
        if (tsv->value_known)
  	printf_filtered ("  %s", plongest (tsv->value));
!       else if (trace_running_p || traceframe_number >= 0)
  	/* The value is/was defined, but we don't have it.  */
  	printf_filtered (_("  <unknown>"));
        else
--- 434,440 ----
        print_spaces_filtered (11 - strlen (plongest (tsv->initial_value)), gdb_stdout);
        if (tsv->value_known)
  	printf_filtered ("  %s", plongest (tsv->value));
!       else if (current_trace_status ()->running || traceframe_number >= 0)
  	/* The value is/was defined, but we don't have it.  */
  	printf_filtered (_("  <unknown>"));
        else
*************** trace_start_command (char *args, int fro
*** 1513,1523 ****
    if (!any_downloaded)
      error ("No tracepoints downloaded, not starting trace");
    
!   /* Init any trace state variables that start with nonzero values.  */
    for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
      {
!       if (tsv->initial_value != 0)
! 	target_download_trace_state_variable (tsv);
      }
    
    /* Tell target to treat text-like sections as transparent.  */
--- 1542,1551 ----
    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)
      {
!       target_download_trace_state_variable (tsv);
      }
    
    /* Tell target to treat text-like sections as transparent.  */
*************** trace_start_command (char *args, int fro
*** 1530,1536 ****
    set_traceframe_num (-1);
    set_tracepoint_num (-1);
    set_traceframe_context (NULL);
!   trace_running_p = 1;
  }
  
  /* tstop command */
--- 1558,1564 ----
    set_traceframe_num (-1);
    set_tracepoint_num (-1);
    set_traceframe_context (NULL);
!   current_trace_status()->running = 1;
  }
  
  /* tstop command */
*************** void
*** 1544,1574 ****
  stop_tracing ()
  {
    target_trace_stop ();
!   trace_running_p = 0;
! }
! 
! unsigned long trace_running_p;
! 
! int
! get_trace_status ()
! {
!   int status = target_get_trace_status (NULL);
! 
!   /* exported for use by the GUI */
!   trace_running_p = (status > 0);
! 
!   return status;
  }
  
  /* tstatus command */
  static void
  trace_status_command (char *args, int from_tty)
  {
!   int status = get_trace_status ();
    
!   if (status < 0)
!     printf_filtered (_("Trace can not be run on the target.\n"));
!   else if (trace_running_p)
      {
        printf_filtered (_("Trace is running on the target.\n"));
        if (disconnected_tracing)
--- 1572,1606 ----
  stop_tracing ()
  {
    target_trace_stop ();
!   /* should change in response to reply? */
!   current_trace_status ()->running = 0;
  }
  
  /* tstatus command */
  static void
  trace_status_command (char *args, int from_tty)
  {
!   struct trace_status *ts = current_trace_status ();
!   int status;
    
!   status = target_get_trace_status (ts);
! 
!   if (status == -1)
!     {
!       if (ts->from_file)
! 	printf_filtered (_("Using a trace file.\n"));
!       else
! 	{
! 	  printf_filtered (_("Trace can not be run on this target.\n"));
! 	  return;
! 	}
!     }
! 
!   if (!ts->running_known)
!     {
!       printf_filtered (_("Run/stop status is unknown.\n"));
!     }
!   else if (ts->running)
      {
        printf_filtered (_("Trace is running on the target.\n"));
        if (disconnected_tracing)
*************** trace_status_command (char *args, int fr
*** 1577,1584 ****
  	printf_filtered (_("Trace will stop if GDB disconnects.\n"));
      }
    else
!     printf_filtered (_("Trace is not running on the target.\n"));
  
    if (traceframe_number >= 0)
      printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
  		     traceframe_number, tracepoint_number);
--- 1609,1657 ----
  	printf_filtered (_("Trace will stop if GDB disconnects.\n"));
      }
    else
!     {
!       switch (ts->stop_reason)
! 	{
! 	case trace_never_run:
! 	  printf_filtered (_("No trace has been run on the target.\n"));
! 	  break;
! 	case tstop_command:
! 	  printf_filtered (_("Trace stopped by a tstop command.\n"));
! 	  break;
! 	case trace_buffer_full:
! 	  printf_filtered (_("Trace stopped because the buffer was full.\n"));
! 	  break;
! 	case trace_disconnected:
! 	  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 trace_stop_reason_unknown:
! 	  printf_filtered (_("Trace stopped for an unknown reason.\n"));
! 	  break;
! 	default:
! 	  printf_filtered (_("Trace stopped for some other reason (%d).\n"),
! 			   ts->stop_reason);
! 	  break;
! 	}
!     }
! 
!   if (ts->traceframe_count >= 0)
!     {
!       printf_filtered (_("Collected %d trace frames.\n"),
! 		       ts->traceframe_count);
!     }
  
+   if (ts->buffer_free)
+     {
+       printf_filtered (_("Trace buffer has %d bytes free.\n"),
+ 		       ts->buffer_free);
+     }
+ 
+   /* Now report on what we're doing with tfind.  */
    if (traceframe_number >= 0)
      printf_filtered (_("Looking at trace frame %d, tracepoint %d.\n"),
  		     traceframe_number, tracepoint_number);
*************** trace_status_command (char *args, int fr
*** 1589,1595 ****
  void
  disconnect_or_stop_tracing (int from_tty)
  {
!   if (trace_running_p && from_tty)
      {
        int cont = query (_("Trace is running.  Continue tracing after detach? "));
        /* Note that we send the query result without affecting the
--- 1662,1675 ----
  void
  disconnect_or_stop_tracing (int from_tty)
  {
!   /* It can happen that the target that was tracing went away on its
!      own, and we didn't notice.  Get a status update, and if the
!      current target doesn't even do tracing, then assume it's not
!      running anymore.  */
!   if (target_get_trace_status (current_trace_status ()) < 0)
!     current_trace_status ()->running = 0;
! 
!   if (current_trace_status ()->running && from_tty)
      {
        int cont = query (_("Trace is running.  Continue tracing after detach? "));
        /* Note that we send the query result without affecting the
*************** finish_tfind_command (enum trace_find_ty
*** 1674,1680 ****
    else
      set_traceframe_context (get_current_frame ());
  
!   if (from_tty)
      {
        enum print_what print_what;
  
--- 1754,1764 ----
    else
      set_traceframe_context (get_current_frame ());
  
!   /* 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.  */
!   if (from_tty
!       && (has_stack_frames () || traceframe_number >= 0))
      {
        enum print_what print_what;
  
*************** trace_find_command (char *args, int from
*** 1715,1721 ****
  { /* this should only be called with a numeric argument */
    int frameno = -1;
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
    
    if (args == 0 || *args == 0)
--- 1799,1805 ----
  { /* this should only be called with a numeric argument */
    int frameno = -1;
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
    
    if (args == 0 || *args == 0)
*************** trace_find_pc_command (char *args, int f
*** 1774,1780 ****
    CORE_ADDR pc;
    char tmp[40];
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
--- 1858,1864 ----
    CORE_ADDR pc;
    char tmp[40];
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
*************** trace_find_tracepoint_command (char *arg
*** 1792,1798 ****
    int tdp;
    struct breakpoint *tp;
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
--- 1876,1882 ----
    int tdp;
    struct breakpoint *tp;
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
*************** trace_find_line_command (char *args, int
*** 1832,1838 ****
    struct cleanup *old_chain;
    char   startpc_str[40], endpc_str[40];
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
--- 1916,1922 ----
    struct cleanup *old_chain;
    char   startpc_str[40], endpc_str[40];
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
*************** trace_find_range_command (char *args, in
*** 1914,1920 ****
    char start_str[40], stop_str[40];
    char *tmp;
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
--- 1998,2004 ----
    char start_str[40], stop_str[40];
    char *tmp;
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
*************** trace_find_outside_command (char *args, 
*** 1948,1954 ****
    char start_str[40], stop_str[40];
    char *tmp;
  
!   if (trace_running_p)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
--- 2032,2038 ----
    char start_str[40], stop_str[40];
    char *tmp;
  
!   if (current_trace_status ()->running && !current_trace_status ()->from_file)
      error ("May not look at trace frames while trace is running.");
  
    if (args == 0 || *args == 0)
*************** trace_dump_command (char *args, int from
*** 2239,2244 ****
--- 2323,2490 ----
    discard_cleanups (old_cleanups);
  }
  
+ extern int trace_regblock_size;
+ 
+ static void
+ trace_save_command (char *args, int from_tty)
+ {
+   char **argv;
+   char *filename = NULL, *pathname;
+   int target_does_save = 0;
+   struct cleanup *cleanup;
+   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;
+   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"));
+ 
+   /* If the target is to save the data to a file on its own, then just
+      send the command and be done with it.  */
+   if (target_does_save)
+     {
+       err = target_save_trace_data (filename);
+       if (err < 0)
+ 	error (_("Target failed to save trace data to '%s'."),
+ 	       filename);
+       return;
+     }
+ 
+   /* Get the trace status first before opening the file, so if the
+      target is losing, we can get out without touching files.  */
+   status = target_get_trace_status (ts);
+ 
+   pathname = tilde_expand (args);
+   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));
+   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);
+ 
+   /* Write descriptive info.  */
+ 
+   /* Write out the size of a register block.  */
+   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);
+ 
+   /* Note that we want to upload tracepoints and save those, rather
+      than simply writing out the local ones, because the user may have
+      changed tracepoints in GDB in preparation for a future tracing
+      run, or maybe just mass-deleted all types of breakpoints as part
+      of cleaning up.  So as not to contaminate the session, leave the
+      data in its uploaded form, don't make into real tracepoints.  */
+ 
+   /* Get trace state variables first, they may be checked when parsing
+      uploaded commands.  */
+ 
+   target_upload_trace_state_variables (&uploaded_tsvs);
+ 
+   for (utsv = uploaded_tsvs; utsv; utsv = utsv->next)
+     {
+       char *buf = "";
+ 
+       if (utsv->name)
+ 	{
+ 	  buf = (char *) xmalloc (strlen (utsv->name) * 2 + 1);
+ 	  bin2hex ((gdb_byte *) (utsv->name), buf, 0);
+ 	}
+ 
+       fprintf (fp, "tsv %x:%s:%x:%s\n",
+ 	       utsv->number, phex_nz (utsv->initial_value, 8),
+ 	       utsv->builtin, buf);
+ 
+       if (utsv->name)
+ 	xfree (buf);
+     }
+ 
+   free_uploaded_tsvs (&uploaded_tsvs);
+ 
+   target_upload_tracepoints (&uploaded_tps);
+ 
+   for (utp = uploaded_tps; utp; utp = utp->next)
+     {
+       fprintf (fp, "tp T%x:%s:%c:%x:%x",
+ 	       utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ 	       (utp->enabled ? 'E' : 'D'), utp->step, utp->pass);
+       if (utp->type == bp_fast_tracepoint)
+ 	fprintf (fp, ":F%x", utp->orig_size);
+       if (utp->cond)
+ 	fprintf (fp, ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
+ 		 utp->cond);
+       fprintf (fp, "\n");
+       for (a = 0; a < utp->numactions; ++a)
+ 	fprintf (fp, "tp A%x:%s:%s\n",
+ 		 utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ 		 utp->actions[a]);
+       for (a = 0; a < utp->num_step_actions; ++a)
+ 	fprintf (fp, "tp S%x:%s:%s\n",
+ 		 utp->number, phex_nz (utp->addr, sizeof (utp->addr)),
+ 		 utp->step_actions[a]);
+     }
+ 
+   free_uploaded_tps (&uploaded_tps);
+ 
+   /* Mark the end of the definition section.  */
+   fprintf (fp, "\n");
+ 
+   /* Get and write the trace data proper.  We ask for big blocks, in
+      the hopes of efficiency, but will take less if the target has
+      packet size limitations or some such.  */
+   while (1)
+     {
+       gotten = target_get_raw_trace_data (buf, offset, MAX_TRACE_UPLOAD);
+       if (gotten < 0)
+ 	error (_("Failure to get requested trace buffer data"));
+       /* No more data is forthcoming, we're done.  */
+       if (gotten == 0)
+ 	break;
+       fwrite (buf, gotten, 1, fp);
+       offset += gotten;
+     }
+ 
+   /* Mark the end of trace data.  */
+   fwrite (&gotten, 4, 1, fp);
+ 
+   do_cleanups (cleanup);
+   if (from_tty)
+     printf_filtered (_("Trace data saved to file '%s'.\n"), args);
+ }
+ 
  /* Tell the target what to do with an ongoing tracing run if GDB
     disconnects for some reason.  */
  
*************** get_traceframe_number (void)
*** 2286,2291 ****
--- 2532,3444 ----
    return traceframe_number;
  }
  
+ 
+ /* Given a number and address, return an uploaded tracepoint with that
+    number, creating if necessary.  */
+ 
+ struct uploaded_tp *
+ get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp)
+ {
+   struct uploaded_tp *utp;
+ 
+   for (utp = *utpp; utp; utp = utp->next)
+     if (utp->number == num && utp->addr == addr)
+       return utp;
+   utp = (struct uploaded_tp *) xmalloc (sizeof (struct uploaded_tp));
+   memset (utp, 0, sizeof (struct uploaded_tp));
+   utp->number = num;
+   utp->addr = addr;
+   utp->next = *utpp;
+   *utpp = utp;
+   return utp;
+ }
+ 
+ static void
+ free_uploaded_tps (struct uploaded_tp **utpp)
+ {
+   struct uploaded_tp *next_one;
+ 
+   while (*utpp)
+     {
+       next_one = (*utpp)->next;
+       xfree (*utpp);
+       *utpp = next_one;
+     }
+ }
+ 
+ /* Given a number and address, return an uploaded tracepoint with that
+    number, creating if necessary.  */
+ 
+ struct uploaded_tsv *
+ get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
+ {
+   struct uploaded_tsv *utsv;
+ 
+   for (utsv = *utsvp; utsv; utsv = utsv->next)
+     if (utsv->number == num)
+       return utsv;
+   utsv = (struct uploaded_tsv *) xmalloc (sizeof (struct uploaded_tsv));
+   memset (utsv, 0, sizeof (struct uploaded_tsv));
+   utsv->number = num;
+   utsv->next = *utsvp;
+   *utsvp = utsv;
+   return utsv;
+ }
+ 
+ static void
+ free_uploaded_tsvs (struct uploaded_tsv **utsvp)
+ {
+   struct uploaded_tsv *next_one;
+ 
+   while (*utsvp)
+     {
+       next_one = (*utsvp)->next;
+       xfree (*utsvp);
+       *utsvp = next_one;
+     }
+ }
+ 
+ /* Look for an existing tracepoint that seems similar enough to the
+    uploaded one.  Enablement isn't compared, because the user can
+    toggle that freely, and may have done so in anticipation of the
+    next trace run.  */
+ 
+ struct breakpoint *
+ find_matching_tracepoint (struct uploaded_tp *utp)
+ {
+   VEC(breakpoint_p) *tp_vec = all_tracepoints ();
+   int ix;
+   struct breakpoint *t;
+   struct bp_location *loc;
+ 
+   for (ix = 0; VEC_iterate (breakpoint_p, tp_vec, ix, t); ix++)
+     {
+       if (t->type == utp->type
+ 	  && t->step_count == utp->step
+ 	  && t->pass_count == utp->pass
+ 	  /* FIXME also test conditionals and actions */
+ 	  )
+ 	{
+ 	  /* Scan the locations for an address match.  */
+ 	  for (loc = t->loc; loc; loc = loc->next)
+ 	    {
+ 	      if (loc->address == utp->addr)
+ 		return t;
+ 	    }
+ 	}
+     }
+   return NULL;
+ }
+ 
+ /* Given a list of tracepoints uploaded from a target, attempt to
+    match them up with existing tracepoints, and create new ones if not
+    found.  */
+ 
+ void
+ merge_uploaded_tracepoints (struct uploaded_tp **uploaded_tps)
+ {
+   struct uploaded_tp *utp;
+   struct breakpoint *t;
+ 
+   /* Look for GDB tracepoints that match up with our uploaded versions.  */
+   for (utp = *uploaded_tps; utp; utp = utp->next)
+     {
+       t = find_matching_tracepoint (utp);
+       if (t)
+ 	printf_filtered (_("Assuming tracepoint %d is same as target's tracepoint %d at %s.\n"),
+ 			 t->number, utp->number, paddress (get_current_arch (), utp->addr));
+       else
+ 	{
+ 	  t = create_tracepoint_from_upload (utp);
+ 	  if (t)
+ 	    printf_filtered (_("Created tracepoint %d for target's tracepoint %d at %s.\n"),
+ 			     t->number, utp->number, paddress (get_current_arch (), utp->addr));
+ 	  else
+ 	    printf_filtered (_("Failed to create tracepoint for target's tracepoint %d at %s, skipping it.\n"),
+ 			     utp->number, paddress (get_current_arch (), utp->addr));
+ 	}
+       /* Whether found or created, record the number used by the
+ 	 target, to help with mapping target tracepoints back to their
+ 	 counterparts here.  */
+       if (t)
+ 	t->number_on_target = utp->number;
+     }
+ 
+   free_uploaded_tps (uploaded_tps);
+ }
+ 
+ /* Trace state variables don't have much to identify them beyond their
+    name, so just use that to detect matches.  */
+ 
+ struct trace_state_variable *
+ find_matching_tsv (struct uploaded_tsv *utsv)
+ {
+   if (!utsv->name)
+     return NULL;
+ 
+   return find_trace_state_variable (utsv->name);
+ }
+ 
+ struct trace_state_variable *
+ create_tsv_from_upload (struct uploaded_tsv *utsv)
+ {
+   const char *namebase;
+   char buf[20];
+   int try_num = 0;
+   struct trace_state_variable *tsv;
+ 
+   if (utsv->name)
+     {
+       namebase = utsv->name;
+       sprintf (buf, "%s", namebase);
+     }
+   else
+     {
+       namebase = "__tsv";
+       sprintf (buf, "%s_%d", namebase, try_num++);
+     }
+ 
+   /* Fish for a name that is not in use.  */
+   /* (should check against all internal vars?) */
+   while (find_trace_state_variable (buf))
+     sprintf (buf, "%s_%d", namebase, try_num++);
+ 
+   /* We have an available name, create the variable.  */
+   tsv = create_trace_state_variable (xstrdup (buf));
+   tsv->initial_value = utsv->initial_value;
+   tsv->builtin = utsv->builtin;
+ 
+   return tsv;
+ }
+ 
+ /* Given a list of uploaded trace state variables, try to match them
+    up with existing variables, or create additional ones.  */
+ 
+ void
+ merge_uploaded_trace_state_variables (struct uploaded_tsv **uploaded_tsvs)
+ {
+   int ix;
+   struct uploaded_tsv *utsv;
+   struct trace_state_variable *tsv;
+   int highest;
+ 
+   /* Most likely some numbers will have to be reassigned as part of
+      the merge, so clear them all in anticipation.  */
+   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+     tsv->number = 0;
+ 
+   for (utsv = *uploaded_tsvs; utsv; utsv = utsv->next)
+     {
+       tsv = find_matching_tsv (utsv);
+       if (tsv)
+ 	printf_filtered (_("Assuming trace state variable $%s is same as target's variable %d.\n"),
+ 			 tsv->name, utsv->number);
+       else
+ 	{
+ 	  tsv = create_tsv_from_upload (utsv);
+ 	  printf_filtered (_("Created trace state variable $%s for target's variable %d.\n"),
+ 			   tsv->name, utsv->number);
+ 	}
+       /* Give precedence to numberings that come from the target.  */
+       if (tsv)
+ 	tsv->number = utsv->number;
+     }
+ 
+   /* Renumber everything that didn't get a target-assigned number.  */
+   highest = 0;
+   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+     if (tsv->number > highest)
+       highest = tsv->number;
+ 
+   ++highest;
+   for (ix = 0; VEC_iterate (tsv_s, tvariables, ix, tsv); ++ix)
+     if (tsv->number == 0)
+       tsv->number = highest++;
+ 
+   free_uploaded_tsvs (uploaded_tsvs);
+ }
+ 
+ /* target tfile command */
+ 
+ struct target_ops tfile_ops;
+ 
+ /* Fill in tfile_ops with its defined operations and properties.  */
+ 
+ #define TRACE_HEADER_SIZE 8
+ 
+ int trace_fd;
+ off_t trace_frames_offset;
+ off_t cur_offset;
+ int cur_data_size;
+ int trace_regblock_size;
+ 
+ static void tfile_interp_line (char *line,
+ 			       struct uploaded_tp **utpp,
+ 			       struct uploaded_tsv **utsvp);
+ 
+ static void
+ tfile_open (char *filename, int from_tty)
+ {
+   char *temp;
+   struct cleanup *old_chain;
+   int flags;
+   int scratch_chan;
+   char header[TRACE_HEADER_SIZE];
+   char linebuf[1000]; /* should be max remote packet size or so */
+   char byte;
+   int bytes, i;
+   struct trace_status *ts;
+   struct uploaded_tp *uploaded_tps = NULL;
+   struct uploaded_tsv *uploaded_tsvs = NULL;
+ 
+   target_preopen (from_tty);
+   if (!filename)
+     error (_("No trace file specified."));
+ 
+   filename = tilde_expand (filename);
+   if (!IS_ABSOLUTE_PATH(filename))
+     {
+       temp = concat (current_directory, "/", filename, (char *)NULL);
+       xfree (filename);
+       filename = temp;
+     }
+ 
+   old_chain = make_cleanup (xfree, filename);
+ 
+   flags = O_BINARY | O_LARGEFILE;
+   flags |= O_RDONLY;
+   scratch_chan = open (filename, flags, 0);
+   if (scratch_chan < 0)
+     perror_with_name (filename);
+ 
+   /* Looks semi-reasonable.  Toss the old trace file and work on the new.  */
+ 
+   discard_cleanups (old_chain);	/* Don't free filename any more */
+   unpush_target (&tfile_ops);
+ 
+   push_target (&tfile_ops);
+   discard_cleanups (old_chain);
+ 
+   trace_fd = scratch_chan;
+ 
+   bytes = 0;
+   /* Read the file header and test for validity.  */
+   read (trace_fd, &header, TRACE_HEADER_SIZE);
+   bytes += TRACE_HEADER_SIZE;
+   if (!(header[0] == 0x7f
+ 	&& (strncmp (header + 1, "TRACE0\n", 7) == 0)))
+     error (_("File is not a valid trace file."));
+ 
+   trace_regblock_size = 0;
+   ts = current_trace_status ();
+   /* We know we're working with a file.  */
+   ts->from_file = 1;
+   /* Set defaults in case there is no status line.  */
+   ts->running_known = 0;
+   ts->stop_reason = trace_stop_reason_unknown;
+   ts->traceframe_count = -1;
+   ts->buffer_free = 0;
+ 
+   /* Read through a section of newline-terminated lines that
+      define things like tracepoints.  */
+   i = 0;
+   while (1)
+     {
+       read (trace_fd, &byte, 1);
+       ++bytes;
+       if (byte == '\n')
+ 	{
+ 	  /* Empty line marks end of the definition section.  */
+ 	  if (i == 0)
+ 	    break;
+ 	  linebuf[i] = '\0';
+ 	  i = 0;
+ 	  tfile_interp_line (linebuf, &uploaded_tps, &uploaded_tsvs);
+ 	}
+       else
+ 	linebuf[i++] = byte;
+       if (i >= 1000)
+ 	error (_("Excessively long lines in trace file"));
+     }
+ 
+   /* Add the file's tracepoints and variables into the current mix.  */
+ 
+   merge_uploaded_tracepoints (&uploaded_tps);
+ 
+   merge_uploaded_trace_state_variables (&uploaded_tsvs);
+ 
+   /* Record the starting offset of the binary trace data.  */
+   trace_frames_offset = bytes;
+ 
+   /* If we don't have a blocksize, we can't interpret the
+      traceframes.  */
+   if (trace_regblock_size == 0)
+     error (_("No register block size recorded in trace file"));
+   if (ts->traceframe_count <= 0)
+     {
+       warning ("No traceframes present in this file.");
+       return;
+     }
+ 
+ #define TFILE_PID (1)
+   inferior_appeared (current_inferior (), TFILE_PID);
+   inferior_ptid = pid_to_ptid (TFILE_PID);
+   add_thread_silent (inferior_ptid);
+ 
+   post_create_inferior (&tfile_ops, from_tty);
+ 
+ #if 0
+   /* FIXME this will get defined in MI patch submission */
+   tfind_1 (tfind_number, 0, 0, 0, 0);
+ #endif
+ }
+ 
+ /* Interpret the given line from the definitions part of the trace
+    file.  */
+ 
+ static void
+ tfile_interp_line (char *line,
+ 		   struct uploaded_tp **utpp, struct uploaded_tsv **utsvp)
+ {
+   char *p = line;
+ 
+   if (strncmp (p, "R ", strlen ("R ")) == 0)
+     {
+       p += strlen ("R ");
+       trace_regblock_size = strtol (p, &p, 16);
+     }
+   else if (strncmp (p, "status ", strlen ("status ")) == 0)
+     {
+       p += strlen ("status ");
+       parse_trace_status (p, current_trace_status ());
+     }
+   else if (strncmp (p, "tp ", strlen ("tp ")) == 0)
+     {
+       p += strlen ("tp ");
+       parse_tracepoint_definition (p, utpp);
+     }
+   else if (strncmp (p, "tsv ", strlen ("tsv ")) == 0)
+     {
+       p += strlen ("tsv ");
+       parse_tsv_definition (p, utsvp);
+     }
+   else
+     warning ("Ignoring trace file definition \"%s\"", line);
+ }
+ 
+ /* Parse the part of trace status syntax that is shared between
+    the remote protocol and the trace file reader.  */
+ 
+ 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;
+   ULONGEST val;
+ 
+   ts->running_known = 1;
+   ts->running = (*p++ == '1');
+   ts->stop_reason = trace_stop_reason_unknown;
+   while (*p++)
+     {
+       p1 = strchr (p, ':');
+       if (p1 == NULL)
+ 	error (_("Malformed line: %s\n\
+ Packet: '%s'\n"), p, line);
+       if (strncmp (p, stop_reason_names[trace_buffer_full], p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->stop_reason = trace_buffer_full;
+ 	}
+       else if (strncmp (p, stop_reason_names[trace_never_run], p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->stop_reason = trace_never_run;
+ 	}
+       else if (strncmp (p, stop_reason_names[tracepoint_passcount], p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->stop_reason = tracepoint_passcount;
+ 	  ts->stopping_tracepoint = val;
+ 	}
+       else if (strncmp (p, stop_reason_names[tstop_command], p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->stop_reason = tstop_command;
+ 	}
+       if (strncmp (p, "tframes", p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->traceframe_count = val;
+ 	}
+       if (strncmp (p, "tfree", p1 - p) == 0)
+ 	{
+ 	  p = unpack_varlen_hex (++p1, &val);
+ 	  ts->buffer_free = val;
+ 	}
+       else
+ 	{
+ 	  /* Silently skip unknown optional info.  */
+ 	  p_temp = strchr (p1 + 1, ';');
+ 	  if (p_temp)
+ 	    p = p_temp;
+ 	}
+     }
+ }
+ 
+ /* Given a line of text defining a tracepoint or tracepoint action, 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;
+   enum bptype type;
+   char *cond;
+   struct uploaded_tp *utp = NULL;
+ 
+   p = line;
+   /* Both tracepoint and action definitions start with the same number
+      and address sequence.  */
+   piece = *p++;
+   p = unpack_varlen_hex (p, &num);
+   p++;  /* skip a colon */
+   p = unpack_varlen_hex (p, &addr);
+   p++;  /* skip a colon */
+   if (piece == 'T')
+     {
+       enabled = (*p++ == 'E');
+       p++;  /* skip a colon */
+       p = unpack_varlen_hex (p, &step);
+       p++;  /* skip a colon */
+       p = unpack_varlen_hex (p, &pass);
+       type = bp_tracepoint;
+       cond = NULL;
+       /* Thumb through optional fields.  */
+       while (*p == ':')
+ 	{
+ 	  p++;  /* skip a colon */
+ 	  if (*p == 'F')
+ 	    {
+ 	      type = bp_fast_tracepoint;
+ 	      p++;
+ 	      p = unpack_varlen_hex (p, &orig_size);
+ 	    }
+ 	  else if (*p == 'X')
+ 	    {
+ 	      p++;
+ 	      p = unpack_varlen_hex (p, &xlen);
+ 	      p++;  /* skip a comma */
+ 	      cond = (char *) xmalloc (2 * xlen + 1);
+ 	      strncpy (cond, p, 2 * xlen);
+ 	      cond[2 * xlen] = '\0';
+ 	      p += 2 * xlen;
+ 	    }
+ 	  else
+ 	    warning ("Unrecognized char '%c' in tracepoint definition, skipping rest", *p);
+ 	}
+       utp = get_uploaded_tp (num, addr, utpp);
+       utp->type = type;
+       utp->enabled = enabled;
+       utp->step = step;
+       utp->pass = pass;
+       utp->cond = cond;
+     }
+   else if (piece == 'A')
+     {
+       utp = get_uploaded_tp (num, addr, utpp);
+       utp->actions[utp->numactions++] = xstrdup (p);
+     }
+   else if (piece == 'S')
+     {
+       utp = get_uploaded_tp (num, addr, utpp);
+       utp->step_actions[utp->num_step_actions++] = xstrdup (p);
+     }
+   else
+     {
+       error ("Invalid tracepoint piece");
+     }
+ }
+ 
+ /* Convert a textual description of a trace state variable into an
+    uploaded object.  */
+ 
+ void
+ parse_tsv_definition (char *line, struct uploaded_tsv **utsvp)
+ {
+   char *p, *buf;
+   ULONGEST num, initval, builtin;
+   int end;
+   struct uploaded_tsv *utsv = NULL;
+ 
+   buf = alloca (strlen (line));
+ 
+   p = line;
+   p = unpack_varlen_hex (p, &num);
+   p++; /* skip a colon */
+   p = unpack_varlen_hex (p, &initval);
+   p++; /* skip a colon */
+   p = unpack_varlen_hex (p, &builtin);
+   p++; /* skip a colon */
+   end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
+   buf[end] = '\0';
+ 
+   utsv = get_uploaded_tsv (num, utsvp);
+   utsv->initial_value = initval;
+   utsv->builtin = builtin;
+   utsv->name = xstrdup (buf);
+ }
+ 
+ /* Discard all vestiges of any previous trace file and mark data and stack
+    spaces as empty.  */
+ 
+ static void
+ tfile_close (int quitting)
+ {
+   /* Nothing to do?? */
+ }
+ 
+ static void
+ tfile_files_info (struct target_ops *t)
+ {
+   printf_filtered ("Looking at a trace file.\n");
+ }
+ 
+ /* The trace status for a file is that tracing can never be run.  */
+ 
+ static int
+ tfile_get_trace_status (struct trace_status *ts)
+ {
+   /* Other bits of trace status were collected as part of opening the
+      trace files, so nothing to do here.  */
+ 
+   return -1;
+ }
+ 
+ /* Given the position of a traceframe in the file, figure out what
+    address the frame was collected at.  This would normally be the
+    value of a collected PC register, but if not available, we
+    improvise.  */
+ 
+ static ULONGEST
+ tfile_get_traceframe_address (off_t tframe_offset)
+ {
+   ULONGEST addr = 0;
+   short tpnum;
+   struct breakpoint *tp;
+   off_t saved_offset = cur_offset;
+ 
+   /* 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);
+   tp = get_tracepoint_by_number_on_target (tpnum);
+   if (tp && tp->loc)
+     addr = tp->loc->address;
+ 
+   /* Restore our seek position.  */
+   cur_offset = saved_offset;
+   lseek (trace_fd, cur_offset, SEEK_SET);
+   return addr;
+ }
+ 
+ /* Given a type of search and some parameters, scan the collection of
+    traceframes in the file looking for a match.  When found, return
+    both the traceframe and tracepoint number, otherwise -1 for
+    each.  */
+ 
+ static int
+ tfile_trace_find (enum trace_find_type type, int num,
+ 		  ULONGEST addr1, ULONGEST addr2, int *tpp)
+ {
+   short tpnum;
+   int tfnum = 0, found = 0;
+   int data_size;
+   struct breakpoint *tp;
+   off_t offset, tframe_offset;
+   ULONGEST tfaddr;
+ 
+   lseek (trace_fd, trace_frames_offset, SEEK_SET);
+   offset = trace_frames_offset;
+   while (1)
+     {
+       tframe_offset = offset;
+       read (trace_fd, &tpnum, 2);
+       offset += 2;
+       if (tpnum == 0)
+ 	break;
+       read (trace_fd, &data_size, 4);	
+       offset += 4;
+       switch (type)
+ 	{
+ 	case tfind_number:
+ 	  if (tfnum == num)
+ 	    found = 1;
+ 	  break;
+ 	case tfind_pc:
+ 	  tfaddr = tfile_get_traceframe_address (tframe_offset);
+ 	  if (tfaddr == addr1)
+ 	    found = 1;
+ 	  break;
+ 	case tfind_tp:
+ 	  tp = get_tracepoint (num);
+ 	  if (tp && tpnum == tp->number_on_target)
+ 	    found = 1;
+ 	  break;
+ 	case tfind_range:
+ 	  tfaddr = tfile_get_traceframe_address (tframe_offset);
+ 	  if (addr1 <= tfaddr && tfaddr <= addr2)
+ 	    found = 1;
+ 	  break;
+ 	case tfind_outside:
+ 	  tfaddr = tfile_get_traceframe_address (tframe_offset);
+ 	  if (!(addr1 <= tfaddr && tfaddr <= addr2))
+ 	    found = 1;
+ 	  break;
+ 	default:
+ 	  internal_error (__FILE__, __LINE__, _("unknown tfind type"));
+ 	}
+       if (found)
+ 	{
+ 	  printf_filtered ("Found traceframe %d.\n", tfnum);
+ 	  if (tpp)
+ 	    *tpp = tpnum;
+ 	  cur_offset = offset;
+ 	  cur_data_size = data_size;
+ 	  return tfnum;
+ 	}
+       /* Skip past the traceframe's data.  */
+       lseek (trace_fd, data_size, SEEK_CUR);
+       offset += data_size;
+       /* Update our own count of traceframes.  */
+       ++tfnum;
+     }
+   /* Did not find what we were looking for.  */
+   if (tpp)
+     *tpp = -1;
+   return -1;
+ }
+ 
+ /* Look for a block of saved registers in the traceframe, and get the
+    requested register from it.  */
+ 
+ static void
+ tfile_fetch_registers (struct target_ops *ops,
+ 		       struct regcache *regcache, int regno)
+ {
+   struct gdbarch *gdbarch = get_regcache_arch (regcache);
+   char block_type;
+   int i, pos, offset, regn, regsize;
+   unsigned short mlen;
+   char *regs;
+ 
+   /* An uninitialized reg size says we're not going to be
+      successful at getting register blocks.  */
+   if (!trace_regblock_size)
+     return;
+ 
+   regs = alloca (trace_regblock_size);
+ 
+   lseek (trace_fd, cur_offset, SEEK_SET);
+   pos = 0;
+   while (pos < cur_data_size)
+     {
+       read (trace_fd, &block_type, 1);
+       ++pos;
+       switch (block_type)
+ 	{
+ 	case 'R':
+ 	  read (trace_fd, regs, trace_regblock_size);
+ 	  /* Assume the block is laid out in GDB register number order,
+ 	     each register with the size that it has in GDB.  */
+ 	  offset = 0;
+ 	  for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+ 	    {
+ 	      regsize = register_size (gdbarch, regn);
+ 	      /* Make sure we stay within block bounds.  */
+ 	      if (offset + regsize >= trace_regblock_size)
+ 		break;
+ 	      if (!regcache_valid_p (regcache, regn))
+ 		{
+ 		  if (regno == regn)
+ 		    {
+ 		      regcache_raw_supply (regcache, regno, regs + offset);
+ 		      break;
+ 		    }
+ 		  else if (regno == -1)
+ 		    {
+ 		      regcache_raw_supply (regcache, regn, regs + offset);
+ 		    }
+ 		}
+ 	      offset += regsize;
+ 	    }
+ 	  return;
+ 	case 'M':
+ 	  lseek (trace_fd, 8, SEEK_CUR);
+ 	  read (trace_fd, &mlen, 2);
+ 	  lseek (trace_fd, mlen, SEEK_CUR);
+ 	  pos += (8 + 2 + mlen);
+ 	  break;
+ 	case 'V':
+ 	  lseek (trace_fd, 4 + 8, SEEK_CUR);
+ 	  pos += (4 + 8);
+ 	  break;
+ 	default:
+ 	  error ("Unknown block type '%c' (0x%x) in trace frame",
+ 		 block_type, block_type);
+ 	  break;
+ 	}
+     }
+ }
+ 
+ static LONGEST
+ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
+ 		    const char *annex, gdb_byte *readbuf,
+ 		    const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+ {
+   char block_type;
+   int pos;
+   ULONGEST maddr;
+   unsigned short mlen;
+ 
+   if (object != TARGET_OBJECT_MEMORY)
+     error ("tfile_xfer_partial: unhandled object type %d", object);
+ 
+   if (readbuf == NULL)
+     error ("tfile_xfer_partial: trace file is read-only");
+ 
+   lseek (trace_fd, cur_offset, SEEK_SET);
+   pos = 0;
+   while (pos < cur_data_size)
+     {
+       read (trace_fd, &block_type, 1);
+       ++pos;
+       switch (block_type)
+ 	{
+ 	case 'R':
+ 	  lseek (trace_fd, trace_regblock_size, SEEK_CUR);
+ 	  pos += trace_regblock_size;
+ 	  break;
+ 	case 'M':
+ 	  read (trace_fd, &maddr, 8);
+ 	  read (trace_fd, &mlen, 2);
+ 	  if (maddr <= offset && (offset + len) <= (maddr + mlen))
+ 	    {
+ 	      read (trace_fd, readbuf, mlen);
+ 	      return mlen;
+ 	    }
+ 	  lseek (trace_fd, mlen, SEEK_CUR);
+ 	  pos += (8 + 2 + mlen);
+ 	  break;
+ 	case 'V':
+ 	  lseek (trace_fd, 4 + 8, SEEK_CUR);
+ 	  pos += (4 + 8);
+ 	  break;
+ 	default:
+ 	  error ("Unknown block type '%c' (0x%x) in traceframe",
+ 		 block_type, block_type);
+ 	  break;
+ 	}
+     }
+   /* Indicate failure to find the requested memory block.  */
+   return -1;
+ }
+ 
+ /* Iterate through the blocks of a trace frame, looking for a 'V'
+    block with a matching tsv number.  */
+ 
+ static int
+ tfile_get_trace_state_variable_value (int tsvnum, LONGEST *val)
+ {
+   char block_type;
+   int pos, vnum;
+   unsigned short mlen;
+ 
+   lseek (trace_fd, cur_offset, SEEK_SET);
+   pos = 0;
+   while (pos < cur_data_size)
+     {
+       read (trace_fd, &block_type, 1);
+       ++pos;
+       switch (block_type)
+ 	{
+ 	case 'R':
+ 	  lseek (trace_fd, trace_regblock_size, SEEK_CUR);
+ 	  pos += trace_regblock_size;
+ 	  break;
+ 	case 'M':
+ 	  lseek (trace_fd, 8, SEEK_CUR);
+ 	  read (trace_fd, &mlen, 2);
+ 	  lseek (trace_fd, mlen, SEEK_CUR);
+ 	  pos += (8 + 2 + mlen);
+ 	  break;
+ 	case 'V':
+ 	  read (trace_fd, &vnum, 4);
+ 	  if (tsvnum == vnum)
+ 	    {
+ 	      read (trace_fd, val, 8);
+ 	      return 1;
+ 	    }
+ 	  lseek (trace_fd, 8, SEEK_CUR);
+ 	  pos += (4 + 8);
+ 	  break;
+ 	default:
+ 	  error ("Unknown block type '%c' (0x%x) in traceframe",
+ 		 block_type, block_type);
+ 	  break;
+ 	}
+     }
+   /* Didn't find anything.  */
+   return 0;
+ }
+ 
+ static int
+ tfile_has_memory (struct target_ops *ops)
+ {
+   return 1;
+ }
+ 
+ static int
+ tfile_has_stack (struct target_ops *ops)
+ {
+   return 1;
+ }
+ 
+ static int
+ tfile_has_registers (struct target_ops *ops)
+ {
+   return 1;
+ }
+ 
+ static void
+ init_tfile_ops (void)
+ {
+   tfile_ops.to_shortname = "tfile";
+   tfile_ops.to_longname = "Local trace dump file";
+   tfile_ops.to_doc =
+     "Use a trace file as a target.  Specify the filename of the trace file.";
+   tfile_ops.to_open = tfile_open;
+   tfile_ops.to_close = tfile_close;
+   tfile_ops.to_fetch_registers = tfile_fetch_registers;
+   tfile_ops.to_xfer_partial = tfile_xfer_partial;
+   tfile_ops.to_files_info = tfile_files_info;
+   tfile_ops.to_get_trace_status = tfile_get_trace_status;
+   tfile_ops.to_trace_find = tfile_trace_find;
+   tfile_ops.to_get_trace_state_variable_value = tfile_get_trace_state_variable_value;
+   /* core_stratum might seem more logical, but GDB doesn't like having
+      more than one core_stratum vector.  */
+   tfile_ops.to_stratum = process_stratum;
+   tfile_ops.to_has_memory = tfile_has_memory;
+   tfile_ops.to_has_stack = tfile_has_stack;
+   tfile_ops.to_has_registers = tfile_has_registers;
+   tfile_ops.to_magic = OPS_MAGIC;
+ }
+ 
  /* module initialization */
  void
  _initialize_tracepoint (void)
*************** _initialize_tracepoint (void)
*** 2332,2337 ****
--- 3485,3495 ----
    add_com ("tdump", class_trace, trace_dump_command,
  	   _("Print everything collected at the current tracepoint."));
  
+   add_com ("tsave", class_trace, trace_save_command, _("\
+ Save the trace data to a file.\n\
+ Use the '-r' option to direct the target to save directly to the file,\n\
+ using its own filesystem."));
+ 
    c = add_com ("tvariable", class_trace, trace_variable_command,_("\
  Define a trace state variable.\n\
  Argument is a $-prefixed name, optionally followed\n\
*************** trace data collected in the meantime."),
*** 2462,2465 ****
--- 3620,3628 ----
  			   NULL,
  			   &setlist,
  			   &showlist);
+ 
+   init_tfile_ops ();
+ 
+   add_target (&tfile_ops);
+ 
  }
Index: tracepoint.h
===================================================================
RCS file: /cvs/src/src/gdb/tracepoint.h,v
retrieving revision 1.19
diff -p -r1.19 tracepoint.h
*** tracepoint.h	7 Jan 2010 19:17:46 -0000	1.19
--- tracepoint.h	12 Jan 2010 01:42:08 -0000
*************** struct trace_state_variable
*** 61,72 ****
  
      /* The value of a variable is a 64-bit signed integer.  */
      LONGEST value;
    };
  
! extern unsigned long trace_running_p;
  
  extern char *default_collect;
  
  /* A hook used to notify the UI of tracepoint operations.  */
  
  extern void (*deprecated_trace_find_hook) (char *arg, int from_tty);
--- 61,139 ----
  
      /* The value of a variable is a 64-bit signed integer.  */
      LONGEST value;
+ 
+     /* This is true for variables that are predefined and built into
+        the target.  */
+     int builtin;
+    };
+ 
+ /* The trace status encompasses various info about the general state
+    of the tracing run.  */
+ 
+ enum trace_stop_reason
+   {
+     trace_stop_reason_unknown,
+     trace_never_run,
+     tstop_command,
+     trace_buffer_full,
+     trace_disconnected,
+     tracepoint_passcount
    };
  
! struct trace_status
! {
!   /* This is true if the status is coming from a file rather
!      than a live target.  */
!   int from_file;
! 
!   /* This is true if the value of the running field is known.  */
!   int running_known;
! 
!   int running;
! 
!   enum trace_stop_reason stop_reason;
! 
!   int stopping_tracepoint;
! 
!   int traceframe_count;
! 
!   size_t buffer_size;
! 
!   size_t buffer_free;
! };
! 
! struct trace_status *current_trace_status (void);
  
  extern char *default_collect;
  
+ /* Struct to collect random info about tracepoints on the target.  */
+ 
+ struct uploaded_tp {
+   int number;
+   enum bptype type;
+   ULONGEST addr;
+   int enabled;
+   int step;
+   int pass;
+   int orig_size;
+   char *cond;
+   int numactions;
+   char *actions[100];
+   int num_step_actions;
+   char *step_actions[100];
+   struct uploaded_tp *next;
+ };
+ 
+ /* Struct recording info about trace state variables on the target.  */
+ 
+ struct uploaded_tsv {
+   const char *name;
+   int number;
+   LONGEST initial_value;
+   int builtin;
+   struct uploaded_tsv *next;
+ };
+ 
  /* A hook used to notify the UI of tracepoint operations.  */
  
  extern void (*deprecated_trace_find_hook) (char *arg, int from_tty);
*************** extern void while_stepping_pseudocommand
*** 81,84 ****
--- 148,162 ----
  
  extern struct trace_state_variable *find_trace_state_variable (const char *name);
  
+ extern void parse_trace_status (char *line, struct trace_status *ts);
+ 
+ extern void parse_tracepoint_definition (char *line, struct uploaded_tp **utpp);
+ extern void parse_tsv_definition (char *line, struct uploaded_tsv **utsvp);
+ 
+ extern struct uploaded_tp *get_uploaded_tp (int num, ULONGEST addr,
+ 					    struct uploaded_tp **utpp);
+ extern struct breakpoint *create_tracepoint_from_upload (struct uploaded_tp *utp);
+ extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp);
+ extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp);
+ 
  #endif	/* TRACEPOINT_H */
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.656
diff -p -r1.656 gdb.texinfo
*** doc/gdb.texinfo	6 Jan 2010 20:31:28 -0000	1.656
--- doc/gdb.texinfo	12 Jan 2010 01:42:08 -0000
*************** support tracepoints as of this writing. 
*** 9292,9303 ****
--- 9292,9308 ----
  packets used to implement tracepoints are described in @ref{Tracepoint
  Packets}.
  
+ It is also possible to get trace data from a file, in a manner reminiscent
+ of corefiles; you specify the filename, and use @code{tfind} to search
+ through the file.  See @ref{Trace Files} for more details.
+ 
  This chapter describes the tracepoint commands and features.
  
  @menu
  * Set Tracepoints::
  * Analyze Collected Data::
  * Tracepoint Variables::
+ * Trace Files::
  @end menu
  
  @node Set Tracepoints
*************** which are managed by the target.
*** 10081,10086 ****
--- 10086,10170 ----
  > end
  @end smallexample
  
+ @node Trace Files
+ @section Using Trace Files
+ @cindex trace files
+ 
+ In some situations, the target running a trace experiment may no
+ longer be available; perhaps it crashed, or the hardware was needed
+ for a different activity.  To handle these cases, you can arrange for
+ the target system to dump trace data into a file with a prescribed
+ format, or save it with a @code{tsave} command, and later use it as a
+ source of trace data via the @code{target tfile} command.
+ 
+ @table @code
+ 
+ @kindex tsave
+ @item tsave [ -r ] @var{filename}
+ Save the trace data to @var{filename}.  By default, this command
+ assumes that @var{filename} refers to the host filesystem, so if
+ necessary @value{GDBN} will copy raw trace data up from the target and
+ then save it.  If the target supports it, you can also supply the
+ optional argument @code{-r} (``remote'') to direct the target to save
+ the data directly into @var{filename} in its filesystem, which may be
+ more efficient if the trace buffer is very large.
+ 
+ @kindex target tfile
+ @kindex tfile
+ @item target tfile @var{filename}
+ Use the given @var{filename} as a source of trace data.  Commands that
+ examine data work as they do with a live target, but it is not
+ possible to run any new trace experiments.  @code{tstatus} will report
+ that you can not run tracing, but will still show the current trace
+ frame.
+ 
+ @end table
+ 
+ The trace file comes in three parts: a header, a textual description
+ section, and a trace frame section with binary data.
+ 
+ The header has the form @code{\x7fTRACE0\n}.  The first byte is
+ @code{0x7f} so as to indicate that the file contains binary data,
+ while the @code{0} is a version number that may have different values
+ in the future.
+ 
+ The description section consists of multiple lines of ASCII text
+ separated by newline characters (@code{0xa}).  The lines may include a
+ variety of optional descriptive or context-setting information, such
+ as tracepoint definitions or register set size.  @value{GDBN} will
+ ignore any line that it does not recognize.  An empty line marks the end
+ of this section.
+ 
+ @c FIXME add some specific types of data
+ 
+ The trace frame section consists of a number of consecutive frames.
+ Each frame begins with a two-byte tracepoint number, followed by a
+ four-byte size giving the amount of data in the frame.  The data in the
+ frame consists of a number of blocks, each introduced by a character
+ indicating its type (at least register, memory, and trace state
+ variable).
+ 
+ @table @code
+ @item R @var{bytes}
+ Register block.  The number and ordering of bytes matches that of a
+ @code{g} packet in the remote protocol.  Note that these are the
+ actual bytes, in target order and @value{GDBN} register order, not a
+ hexadecimal encoding.
+ 
+ @item M @var{address} @var{length} @var{bytes}...
+ Memory block. This is a contiguous block of memory, at the 8-byte
+ address @var{address}, with a 2-byte length @var{length}, followed by
+ @var{length} bytes.
+ 
+ @item V @var{number} @var{value}
+ Trace state variable block.  This records the 8-byte signed value
+ @var{value} of trace state variable numbered @var{number}.
+ 
+ @end table
+ 
+ Future enhancements of the trace file format may include additional types
+ of blocks.
+ 
  @node Overlays
  @chapter Debugging Programs That Use Overlays
  @cindex overlays
*************** encoded).  @value{GDBN} will continue to
*** 29762,29771 ****
  (if available), until the target ceases to request them.
  @end table
  
  @item QTDisconnected
  @itemx QTDP
  @itemx QTDV
! @itemx QTfP
  @itemx QTFrame
  @xref{Tracepoint Packets}.
  
--- 29846,29857 ----
  (if available), until the target ceases to request them.
  @end table
  
+ @item qTBuffer
  @item QTDisconnected
  @itemx QTDP
  @itemx QTDV
! @itemx qTfP
! @itemx qTfV
  @itemx QTFrame
  @xref{Tracepoint Packets}.
  
*************** the command by a @samp{,}, not a @samp{:
*** 29794,29800 ****
  conventions above.  Please don't use this packet as a model for new
  packets.)
  
! @item QTsP
  @itemx QTStart    
  @itemx QTStop     
  @itemx QTinit     
--- 29880,29888 ----
  conventions above.  Please don't use this packet as a model for new
  packets.)
  
! @item QTSave
! @item qTsP
! @item qTsV
  @itemx QTStart    
  @itemx QTStop     
  @itemx QTinit     
*************** of data, and multiple @code{qTsP} to get
*** 30244,30249 ****
--- 30332,30360 ----
  to these packets generally take the form of the @code{QTDP} packets
  that define tracepoints. (FIXME add detailed syntax)
  
+ @item qTfV
+ @itemx qTsV
+ These packets request data about trace state variables that are on the
+ target.  @value{GDBN} sends @code{qTfV} to get the first vari of data,
+ and multiple @code{qTsV} to get additional variables.  Replies to
+ these packets follow the syntax of the @code{QTDV} packets that define
+ trace state variables.
+ 
+ @item QTSave:@var{filename}
+ This packet directs the target to save trace data to the file name
+ @var{filename} in the target's filesystem.  @var{filename} is encoded
+ as a hex string; the interpretation of the file name (relative vs
+ absolute, wild cards, etc) is up to the target.
+ 
+ @item qTBuffer:@var{offset},@var{len}
+ Return up to @var{len} bytes of the current contents of trace buffer,
+ starting at @var{offset}.  The trace buffer is treated as if it were
+ a contiguous collection of traceframes, as per the trace file format.
+ The reply consists as many hex-encoded bytes as the target can deliver
+ in a packet; it is not an error to return fewer than were asked for.
+ A reply consisting of just @code{l} indicates that no bytes are
+ available.
+ 
  @end table
  
  @node Host I/O Packets

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