hurd: add gnu_target pointer to fix thread API calls
[deliverable/binutils-gdb.git] / gdb / tracefile-tfile.c
index 8a53e9d6eb7a4723318596b3ed38d2c49b660f9a..ea19177475179f741289353995d82cf858a78e48 100644 (file)
@@ -1,6 +1,6 @@
 /* Trace file TFILE format support in GDB.
 
-   Copyright (C) 1997-2014 Free Software Foundation, Inc.
+   Copyright (C) 1997-2020 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "defs.h"
 #include "tracefile.h"
 #include "readline/tilde.h"
-#include "filestuff.h"
-#include "rsp-low.h" /* bin2hex */
+#include "gdbsupport/filestuff.h"
+#include "gdbsupport/rsp-low.h" /* bin2hex */
+#include "regcache.h"
+#include "inferior.h"
+#include "gdbthread.h"
+#include "exec.h" /* exec_bfd */
+#include "completer.h"
+#include "filenames.h"
+#include "remote.h"
+#include "xml-tdesc.h"
+#include "target-descriptions.h"
+#include "gdbsupport/buffer.h"
+#include "gdbsupport/pathstuff.h"
+#include <algorithm>
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+
+/* The tfile target.  */
+
+static const target_info tfile_target_info = {
+  "tfile",
+  N_("Local trace dump file"),
+  N_("Use a trace file as a target.\n\
+Specify the filename of the trace file.")
+};
+
+class tfile_target final : public tracefile_target
+{
+ public:
+  const target_info &info () const override
+  { return tfile_target_info; }
+
+  void close () override;
+  void fetch_registers (struct regcache *, int) override;
+  enum target_xfer_status xfer_partial (enum target_object object,
+                                               const char *annex,
+                                               gdb_byte *readbuf,
+                                               const gdb_byte *writebuf,
+                                               ULONGEST offset, ULONGEST len,
+                                               ULONGEST *xfered_len) override;
+  void files_info () override;
+  int trace_find (enum trace_find_type type, int num,
+                         CORE_ADDR addr1, CORE_ADDR addr2, int *tpp) override;
+  bool get_trace_state_variable_value (int tsv, LONGEST *val) override;
+  traceframe_info_up traceframe_info () override;
+
+  void get_tracepoint_status (struct breakpoint *tp,
+                             struct uploaded_tp *utp) override;
+};
 
 /* TFILE trace writer.  */
 
@@ -74,7 +123,7 @@ tfile_start (struct trace_file_writer *self, const char *filename)
     = (struct tfile_trace_file_writer *) self;
 
   writer->pathname = tilde_expand (filename);
-  writer->fp = gdb_fopen_cloexec (writer->pathname, "wb");
+  writer->fp = gdb_fopen_cloexec (writer->pathname, "wb").release ();
   if (writer->fp == NULL)
     error (_("Unable to open file '%s' for saving trace data (%s)"),
           writer->pathname, safe_strerror (errno));
@@ -123,7 +172,7 @@ tfile_write_status (struct trace_file_writer *self,
   fprintf (writer->fp, "status %c;%s",
           (ts->running ? '1' : '0'), stop_reason_names[ts->stop_reason]);
   if (ts->stop_reason == tracepoint_error
-      || ts->stop_reason == tstop_command)
+      || ts->stop_reason == trace_stop_command)
     {
       char *buf = (char *) alloca (strlen (ts->stop_desc) * 2 + 1);
 
@@ -177,7 +226,7 @@ static void
 tfile_write_uploaded_tsv (struct trace_file_writer *self,
                          struct uploaded_tsv *utsv)
 {
-  char *buf = "";
+  char *buf = NULL;
   struct tfile_trace_file_writer *writer
     = (struct tfile_trace_file_writer *) self;
 
@@ -189,7 +238,7 @@ tfile_write_uploaded_tsv (struct trace_file_writer *self,
 
   fprintf (writer->fp, "tsv %x:%s:%x:%s\n",
           utsv->number, phex_nz (utsv->initial_value, 8),
-          utsv->builtin, buf);
+          utsv->builtin, buf != NULL ? buf : "");
 
   if (utsv->name)
     xfree (buf);
@@ -206,8 +255,6 @@ tfile_write_uploaded_tp (struct trace_file_writer *self,
 {
   struct tfile_trace_file_writer *writer
     = (struct tfile_trace_file_writer *) self;
-  int a;
-  char *act;
   char buf[MAX_TRACE_UPLOAD];
 
   fprintf (writer->fp, "tp T%x:%s:%c:%x:%x",
@@ -217,31 +264,32 @@ tfile_write_uploaded_tp (struct trace_file_writer *self,
     fprintf (writer->fp, ":F%x", utp->orig_size);
   if (utp->cond)
     fprintf (writer->fp,
-            ":X%x,%s", (unsigned int) strlen (utp->cond) / 2,
-            utp->cond);
+            ":X%x,%s", (unsigned int) strlen (utp->cond.get ()) / 2,
+            utp->cond.get ());
   fprintf (writer->fp, "\n");
-  for (a = 0; VEC_iterate (char_ptr, utp->actions, a, act); ++a)
+  for (const auto &act : utp->actions)
     fprintf (writer->fp, "tp A%x:%s:%s\n",
-            utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
-  for (a = 0; VEC_iterate (char_ptr, utp->step_actions, a, act); ++a)
+            utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act.get ());
+  for (const auto &act : utp->step_actions)
     fprintf (writer->fp, "tp S%x:%s:%s\n",
-            utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act);
+            utp->number, phex_nz (utp->addr, sizeof (utp->addr)), act.get ());
   if (utp->at_string)
     {
       encode_source_string (utp->number, utp->addr,
-                           "at", utp->at_string, buf, MAX_TRACE_UPLOAD);
+                           "at", utp->at_string.get (),
+                           buf, MAX_TRACE_UPLOAD);
       fprintf (writer->fp, "tp Z%s\n", buf);
     }
   if (utp->cond_string)
     {
       encode_source_string (utp->number, utp->addr,
-                           "cond", utp->cond_string,
+                           "cond", utp->cond_string.get (),
                            buf, MAX_TRACE_UPLOAD);
       fprintf (writer->fp, "tp Z%s\n", buf);
     }
-  for (a = 0; VEC_iterate (char_ptr, utp->cmd_strings, a, act); ++a)
+  for (const auto &act : utp->cmd_strings)
     {
-      encode_source_string (utp->number, utp->addr, "cmd", act,
+      encode_source_string (utp->number, utp->addr, "cmd", act.get (),
                            buf, MAX_TRACE_UPLOAD);
       fprintf (writer->fp, "tp Z%s\n", buf);
     }
@@ -252,6 +300,42 @@ tfile_write_uploaded_tp (struct trace_file_writer *self,
                    sizeof (utp->traceframe_usage)));
 }
 
+/* This is the implementation of trace_file_write_ops method
+   write_tdesc.  */
+
+static void
+tfile_write_tdesc (struct trace_file_writer *self)
+{
+  struct tfile_trace_file_writer *writer
+    = (struct tfile_trace_file_writer *) self;
+
+  gdb::optional<std::string> tdesc
+    = target_fetch_description_xml (current_top_target ());
+
+  if (!tdesc)
+    return;
+
+  const char *ptr = tdesc->c_str ();
+
+  /* Write tdesc line by line, prefixing each line with "tdesc ".  */
+  while (ptr != NULL)
+    {
+      const char *next = strchr (ptr, '\n');
+      if (next != NULL)
+       {
+         fprintf (writer->fp, "tdesc %.*s\n", (int) (next - ptr), ptr);
+         /* Skip the \n.  */
+         next++;
+       }
+      else if (*ptr != '\0')
+       {
+         /* Last line, doesn't have a newline.  */
+         fprintf (writer->fp, "tdesc %s\n", ptr);
+       }
+      ptr = next;
+    }
+}
+
 /* This is the implementation of trace_file_write_ops method
    write_definition_end.  */
 
@@ -305,6 +389,7 @@ static const struct trace_file_write_ops tfile_write_ops =
   tfile_write_status,
   tfile_write_uploaded_tsv,
   tfile_write_uploaded_tp,
+  tfile_write_tdesc,
   tfile_write_definition_end,
   tfile_write_raw_data,
   NULL,
@@ -317,7 +402,7 @@ struct trace_file_writer *
 tfile_trace_file_writer_new (void)
 {
   struct tfile_trace_file_writer *writer
-    = xmalloc (sizeof (struct tfile_trace_file_writer));
+    = XNEW (struct tfile_trace_file_writer);
 
   writer->base.ops = &tfile_write_ops;
   writer->fp = NULL;
@@ -325,3 +410,730 @@ tfile_trace_file_writer_new (void)
 
   return (struct trace_file_writer *) writer;
 }
+
+/* target tfile command */
+
+static tfile_target tfile_ops;
+
+#define TRACE_HEADER_SIZE 8
+
+#define TFILE_PID (1)
+
+static char *trace_filename;
+static int trace_fd = -1;
+static off_t trace_frames_offset;
+static off_t cur_offset;
+static int cur_data_size;
+int trace_regblock_size;
+static struct buffer trace_tdesc;
+
+static void tfile_append_tdesc_line (const char *line);
+static void tfile_interp_line (char *line,
+                              struct uploaded_tp **utpp,
+                              struct uploaded_tsv **utsvp);
+
+/* Read SIZE bytes into READBUF from the trace frame, starting at
+   TRACE_FD's current position.  Note that this call `read'
+   underneath, hence it advances the file's seek position.  Throws an
+   error if the `read' syscall fails, or less than SIZE bytes are
+   read.  */
+
+static void
+tfile_read (gdb_byte *readbuf, int size)
+{
+  int gotten;
+
+  gotten = read (trace_fd, readbuf, size);
+  if (gotten < 0)
+    perror_with_name (trace_filename);
+  else if (gotten < size)
+    error (_("Premature end of file while reading trace file"));
+}
+
+/* Open the tfile target.  */
+
+static void
+tfile_target_open (const char *arg, int from_tty)
+{
+  int flags;
+  int scratch_chan;
+  char header[TRACE_HEADER_SIZE];
+  char linebuf[1000]; /* Should be max remote packet size or so.  */
+  gdb_byte 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 (!arg)
+    error (_("No trace file specified."));
+
+  gdb::unique_xmalloc_ptr<char> filename (tilde_expand (arg));
+  if (!IS_ABSOLUTE_PATH (filename.get ()))
+    filename = gdb_abspath (filename.get ());
+
+  flags = O_BINARY | O_LARGEFILE;
+  flags |= O_RDONLY;
+  scratch_chan = gdb_open_cloexec (filename.get (), flags, 0);
+  if (scratch_chan < 0)
+    perror_with_name (filename.get ());
+
+  /* Looks semi-reasonable.  Toss the old trace file and work on the new.  */
+
+  unpush_target (&tfile_ops);
+
+  trace_filename = filename.release ();
+  trace_fd = scratch_chan;
+
+  /* Make sure this is clear.  */
+  buffer_free (&trace_tdesc);
+
+  bytes = 0;
+  /* Read the file header and test for validity.  */
+  tfile_read ((gdb_byte *) &header, TRACE_HEADER_SIZE);
+
+  bytes += TRACE_HEADER_SIZE;
+  if (!(header[0] == 0x7f
+       && (startswith (header + 1, "TRACE0\n"))))
+    error (_("File is not a valid trace file."));
+
+  push_target (&tfile_ops);
+
+  trace_regblock_size = 0;
+  ts = current_trace_status ();
+  /* We know we're working with a file.  Record its name.  */
+  ts->filename = trace_filename;
+  /* 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;
+  ts->disconnected_tracing = 0;
+  ts->circular_buffer = 0;
+
+  try
+    {
+      /* Read through a section of newline-terminated lines that
+        define things like tracepoints.  */
+      i = 0;
+      while (1)
+       {
+         tfile_read (&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"));
+       }
+
+      /* By now, tdesc lines have been read from tfile - let's parse them.  */
+      target_find_description ();
+
+      /* 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"));
+    }
+  catch (const gdb_exception &ex)
+    {
+      /* Remove the partially set up target.  */
+      unpush_target (&tfile_ops);
+      throw;
+    }
+
+  inferior_appeared (current_inferior (), TFILE_PID);
+  inferior_ptid = ptid_t (TFILE_PID);
+  add_thread_silent (&tfile_ops, inferior_ptid);
+
+  if (ts->traceframe_count <= 0)
+    warning (_("No traceframes present in this file."));
+
+  /* Add the file's tracepoints and variables into the current mix.  */
+
+  /* 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);
+
+  post_create_inferior (&tfile_ops, from_tty);
+}
+
+/* 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 (startswith (p, "R "))
+    {
+      p += strlen ("R ");
+      trace_regblock_size = strtol (p, &p, 16);
+    }
+  else if (startswith (p, "status "))
+    {
+      p += strlen ("status ");
+      parse_trace_status (p, current_trace_status ());
+    }
+  else if (startswith (p, "tp "))
+    {
+      p += strlen ("tp ");
+      parse_tracepoint_definition (p, utpp);
+    }
+  else if (startswith (p, "tsv "))
+    {
+      p += strlen ("tsv ");
+      parse_tsv_definition (p, utsvp);
+    }
+  else if (startswith (p, "tdesc "))
+    {
+      p += strlen ("tdesc ");
+      tfile_append_tdesc_line (p);
+    }
+  else
+    warning (_("Ignoring trace file definition \"%s\""), line);
+}
+
+/* Close the trace file and generally clean up.  */
+
+void
+tfile_target::close ()
+{
+  gdb_assert (trace_fd != -1);
+
+  inferior_ptid = null_ptid;   /* Avoid confusion from thread stuff.  */
+  exit_inferior_silent (current_inferior ());
+
+  ::close (trace_fd);
+  trace_fd = -1;
+  xfree (trace_filename);
+  trace_filename = NULL;
+  buffer_free (&trace_tdesc);
+
+  trace_reset_local_state ();
+}
+
+void
+tfile_target::files_info ()
+{
+  printf_filtered ("\t`%s'\n", trace_filename);
+}
+
+void
+tfile_target::get_tracepoint_status (struct breakpoint *tp, struct uploaded_tp *utp)
+{
+  /* Other bits of trace status were collected as part of opening the
+     trace files, so nothing to do here.  */
+}
+
+/* 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 CORE_ADDR
+tfile_get_traceframe_address (off_t tframe_offset)
+{
+  CORE_ADDR addr = 0;
+  short tpnum;
+  struct tracepoint *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);
+  tfile_read ((gdb_byte *) &tpnum, 2);
+  tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
+                                         gdbarch_byte_order
+                                             (target_gdbarch ()));
+
+  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;
+
+  /* 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.  */
+
+int
+tfile_target::trace_find (enum trace_find_type type, int num,
+                         CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
+{
+  short tpnum;
+  int tfnum = 0, found = 0;
+  unsigned int data_size;
+  struct tracepoint *tp;
+  off_t offset, tframe_offset;
+  CORE_ADDR tfaddr;
+
+  if (num == -1)
+    {
+      if (tpp)
+        *tpp = -1;
+      return -1;
+    }
+
+  lseek (trace_fd, trace_frames_offset, SEEK_SET);
+  offset = trace_frames_offset;
+  while (1)
+    {
+      tframe_offset = offset;
+      tfile_read ((gdb_byte *) &tpnum, 2);
+      tpnum = (short) extract_signed_integer ((gdb_byte *) &tpnum, 2,
+                                             gdbarch_byte_order
+                                                 (target_gdbarch ()));
+      offset += 2;
+      if (tpnum == 0)
+       break;
+      tfile_read ((gdb_byte *) &data_size, 4);
+      data_size = (unsigned int) extract_unsigned_integer
+                                     ((gdb_byte *) &data_size, 4,
+                                     gdbarch_byte_order (target_gdbarch ()));
+      offset += 4;
+
+      if (type == tfind_number)
+       {
+         /* Looking for a specific trace frame.  */
+         if (tfnum == num)
+           found = 1;
+       }
+      else
+       {
+         /* Start from the _next_ trace frame.  */
+         if (tfnum > get_traceframe_number ())
+           {
+             switch (type)
+               {
+               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)
+       {
+         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;
+}
+
+/* Prototype of the callback passed to tframe_walk_blocks.  */
+typedef int (*walk_blocks_callback_func) (char blocktype, void *data);
+
+/* Callback for traceframe_walk_blocks, used to find a given block
+   type in a traceframe.  */
+
+static int
+match_blocktype (char blocktype, void *data)
+{
+  char *wantedp = (char *) data;
+
+  if (*wantedp == blocktype)
+    return 1;
+
+  return 0;
+}
+
+/* Walk over all traceframe block starting at POS offset from
+   CUR_OFFSET, and call CALLBACK for each block found, passing in DATA
+   unmodified.  If CALLBACK returns true, this returns the position in
+   the traceframe where the block is found, relative to the start of
+   the traceframe (cur_offset).  Returns -1 if no callback call
+   returned true, indicating that all blocks have been walked.  */
+
+static int
+traceframe_walk_blocks (walk_blocks_callback_func callback,
+                       int pos, void *data)
+{
+  /* Iterate through a traceframe's blocks, looking for a block of the
+     requested type.  */
+
+  lseek (trace_fd, cur_offset + pos, SEEK_SET);
+  while (pos < cur_data_size)
+    {
+      unsigned short mlen;
+      char block_type;
+
+      tfile_read ((gdb_byte *) &block_type, 1);
+
+      ++pos;
+
+      if ((*callback) (block_type, data))
+       return pos;
+
+      switch (block_type)
+       {
+       case 'R':
+         lseek (trace_fd, cur_offset + pos + trace_regblock_size, SEEK_SET);
+         pos += trace_regblock_size;
+         break;
+       case 'M':
+         lseek (trace_fd, cur_offset + pos + 8, SEEK_SET);
+         tfile_read ((gdb_byte *) &mlen, 2);
+          mlen = (unsigned short)
+                extract_unsigned_integer ((gdb_byte *) &mlen, 2,
+                                          gdbarch_byte_order
+                                              (target_gdbarch ()));
+         lseek (trace_fd, mlen, SEEK_CUR);
+         pos += (8 + 2 + mlen);
+         break;
+       case 'V':
+         lseek (trace_fd, cur_offset + pos + 4 + 8, SEEK_SET);
+         pos += (4 + 8);
+         break;
+       default:
+         error (_("Unknown block type '%c' (0x%x) in trace frame"),
+                block_type, block_type);
+         break;
+       }
+    }
+
+  return -1;
+}
+
+/* Convenience wrapper around traceframe_walk_blocks.  Looks for the
+   position offset of a block of type TYPE_WANTED in the current trace
+   frame, starting at POS.  Returns -1 if no such block was found.  */
+
+static int
+traceframe_find_block_type (char type_wanted, int pos)
+{
+  return traceframe_walk_blocks (match_blocktype, pos, &type_wanted);
+}
+
+/* Look for a block of saved registers in the traceframe, and get the
+   requested register from it.  */
+
+void
+tfile_target::fetch_registers (struct regcache *regcache, int regno)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  int offset, regn, regsize, dummy;
+
+  /* An uninitialized reg size says we're not going to be
+     successful at getting register blocks.  */
+  if (!trace_regblock_size)
+    return;
+
+  if (traceframe_find_block_type ('R', 0) >= 0)
+    {
+      gdb_byte *regs = (gdb_byte *) alloca (trace_regblock_size);
+
+      tfile_read (regs, trace_regblock_size);
+
+      for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
+       {
+         if (!remote_register_number_and_offset (regcache->arch (),
+                                                 regn, &dummy, &offset))
+           continue;
+
+         regsize = register_size (gdbarch, regn);
+         /* Make sure we stay within block bounds.  */
+         if (offset + regsize > trace_regblock_size)
+           break;
+         if (regcache->get_register_status (regn) == REG_UNKNOWN)
+           {
+             if (regno == regn)
+               {
+                 regcache->raw_supply (regno, regs + offset);
+                 break;
+               }
+             else if (regno == -1)
+               {
+                 regcache->raw_supply (regn, regs + offset);
+               }
+           }
+       }
+    }
+  else
+    tracefile_fetch_registers (regcache, regno);
+}
+
+static enum target_xfer_status
+tfile_xfer_partial_features (const char *annex,
+                            gdb_byte *readbuf, const gdb_byte *writebuf,
+                            ULONGEST offset, ULONGEST len,
+                            ULONGEST *xfered_len)
+{
+  if (strcmp (annex, "target.xml"))
+    return TARGET_XFER_E_IO;
+
+  if (readbuf == NULL)
+    error (_("tfile_xfer_partial: tdesc is read-only"));
+
+  if (trace_tdesc.used_size == 0)
+    return TARGET_XFER_E_IO;
+
+  if (offset >= trace_tdesc.used_size)
+    return TARGET_XFER_EOF;
+
+  if (len > trace_tdesc.used_size - offset)
+    len = trace_tdesc.used_size - offset;
+
+  memcpy (readbuf, trace_tdesc.buffer + offset, len);
+  *xfered_len = len;
+
+  return TARGET_XFER_OK;
+}
+
+enum target_xfer_status
+tfile_target::xfer_partial (enum target_object object,
+                           const char *annex, gdb_byte *readbuf,
+                           const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
+                           ULONGEST *xfered_len)
+{
+  /* We're only doing regular memory and tdesc for now.  */
+  if (object == TARGET_OBJECT_AVAILABLE_FEATURES)
+    return tfile_xfer_partial_features (annex, readbuf, writebuf,
+                                       offset, len, xfered_len);
+  if (object != TARGET_OBJECT_MEMORY)
+    return TARGET_XFER_E_IO;
+
+  if (readbuf == NULL)
+    error (_("tfile_xfer_partial: trace file is read-only"));
+
+  if (get_traceframe_number () != -1)
+    {
+      int pos = 0;
+      enum target_xfer_status res;
+      /* Records the lowest available address of all blocks that
+        intersects the requested range.  */
+      ULONGEST low_addr_available = 0;
+
+      /* Iterate through the traceframe's blocks, looking for
+        memory.  */
+      while ((pos = traceframe_find_block_type ('M', pos)) >= 0)
+       {
+         ULONGEST maddr, amt;
+         unsigned short mlen;
+         enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
+
+         tfile_read ((gdb_byte *) &maddr, 8);
+         maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
+                                           byte_order);
+         tfile_read ((gdb_byte *) &mlen, 2);
+         mlen = (unsigned short)
+           extract_unsigned_integer ((gdb_byte *) &mlen, 2, byte_order);
+
+         /* If the block includes the first part of the desired
+            range, return as much it has; GDB will re-request the
+            remainder, which might be in a different block of this
+            trace frame.  */
+         if (maddr <= offset && offset < (maddr + mlen))
+           {
+             amt = (maddr + mlen) - offset;
+             if (amt > len)
+               amt = len;
+
+             if (maddr != offset)
+               lseek (trace_fd, offset - maddr, SEEK_CUR);
+             tfile_read (readbuf, amt);
+             *xfered_len = amt;
+             return TARGET_XFER_OK;
+           }
+
+         if (offset < maddr && maddr < (offset + len))
+           if (low_addr_available == 0 || low_addr_available > maddr)
+             low_addr_available = maddr;
+
+         /* Skip over this block.  */
+         pos += (8 + 2 + mlen);
+       }
+
+      /* Requested memory is unavailable in the context of traceframes,
+        and this address falls within a read-only section, fallback
+        to reading from executable, up to LOW_ADDR_AVAILABLE.  */
+      if (offset < low_addr_available)
+       len = std::min (len, low_addr_available - offset);
+      res = exec_read_partial_read_only (readbuf, offset, len, xfered_len);
+
+      if (res == TARGET_XFER_OK)
+       return TARGET_XFER_OK;
+      else
+       {
+         /* No use trying further, we know some memory starting
+            at MEMADDR isn't available.  */
+         *xfered_len = len;
+         return TARGET_XFER_UNAVAILABLE;
+       }
+    }
+  else
+    {
+      /* Fallback to reading from read-only sections.  */
+      return section_table_read_available_memory (readbuf, offset, len,
+                                                 xfered_len);
+    }
+}
+
+/* Iterate through the blocks of a trace frame, looking for a 'V'
+   block with a matching tsv number.  */
+
+bool
+tfile_target::get_trace_state_variable_value (int tsvnum, LONGEST *val)
+{
+  int pos;
+  bool found = false;
+
+  /* Iterate over blocks in current frame and find the last 'V'
+     block in which tsv number is TSVNUM.  In one trace frame, there
+     may be multiple 'V' blocks created for a given trace variable,
+     and the last matched 'V' block contains the updated value.  */
+  pos = 0;
+  while ((pos = traceframe_find_block_type ('V', pos)) >= 0)
+    {
+      int vnum;
+
+      tfile_read ((gdb_byte *) &vnum, 4);
+      vnum = (int) extract_signed_integer ((gdb_byte *) &vnum, 4,
+                                          gdbarch_byte_order
+                                          (target_gdbarch ()));
+      if (tsvnum == vnum)
+       {
+         tfile_read ((gdb_byte *) val, 8);
+         *val = extract_signed_integer ((gdb_byte *) val, 8,
+                                        gdbarch_byte_order
+                                        (target_gdbarch ()));
+         found = true;
+       }
+      pos += (4 + 8);
+    }
+
+  return found;
+}
+
+/* Callback for traceframe_walk_blocks.  Builds a traceframe_info
+   object for the tfile target's current traceframe.  */
+
+static int
+build_traceframe_info (char blocktype, void *data)
+{
+  struct traceframe_info *info = (struct traceframe_info *) data;
+
+  switch (blocktype)
+    {
+    case 'M':
+      {
+       ULONGEST maddr;
+       unsigned short mlen;
+
+       tfile_read ((gdb_byte *) &maddr, 8);
+       maddr = extract_unsigned_integer ((gdb_byte *) &maddr, 8,
+                                         gdbarch_byte_order
+                                         (target_gdbarch ()));
+       tfile_read ((gdb_byte *) &mlen, 2);
+       mlen = (unsigned short)
+               extract_unsigned_integer ((gdb_byte *) &mlen,
+                                         2, gdbarch_byte_order
+                                         (target_gdbarch ()));
+
+       info->memory.emplace_back (maddr, mlen);
+       break;
+      }
+    case 'V':
+      {
+       int vnum;
+
+       tfile_read ((gdb_byte *) &vnum, 4);
+       info->tvars.push_back (vnum);
+      }
+    case 'R':
+    case 'S':
+      {
+       break;
+      }
+    default:
+      warning (_("Unhandled trace block type (%d) '%c ' "
+                "while building trace frame info."),
+              blocktype, blocktype);
+      break;
+    }
+
+  return 0;
+}
+
+traceframe_info_up
+tfile_target::traceframe_info ()
+{
+  traceframe_info_up info (new struct traceframe_info);
+
+  traceframe_walk_blocks (build_traceframe_info, 0, info.get ());
+
+  return info;
+}
+
+/* Handles tdesc lines from tfile by appending the payload to
+   a global trace_tdesc variable.  */
+
+static void
+tfile_append_tdesc_line (const char *line)
+{
+  buffer_grow_str (&trace_tdesc, line);
+  buffer_grow_str (&trace_tdesc, "\n");
+}
+
+void _initialize_tracefile_tfile ();
+void
+_initialize_tracefile_tfile ()
+{
+  add_target (tfile_target_info, tfile_target_open, filename_completer);
+}
This page took 0.033102 seconds and 4 git commands to generate.