Multi-target support
[deliverable/binutils-gdb.git] / gdb / tracefile-tfile.c
index dc7b05a05ff16688080abe0f0f2d07df5c82906f..c1a4a49337246e34d9586e4a2a8d8db836002d92 100644 (file)
@@ -1,6 +1,6 @@
 /* Trace file TFILE format support in GDB.
 
-   Copyright (C) 1997-2016 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.  */
 
 struct tfile_trace_file_writer
@@ -84,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));
@@ -133,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);
 
@@ -187,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;
 
@@ -199,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);
@@ -216,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",
@@ -227,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);
     }
@@ -262,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.  */
 
@@ -315,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,
@@ -338,9 +413,7 @@ tfile_trace_file_writer_new (void)
 
 /* target tfile command */
 
-static struct target_ops tfile_ops;
-
-/* Fill in tfile_ops with its defined operations and properties.  */
+static tfile_target tfile_ops;
 
 #define TRACE_HEADER_SIZE 8
 
@@ -352,7 +425,9 @@ 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);
@@ -375,11 +450,11 @@ tfile_read (gdb_byte *readbuf, int size)
     error (_("Premature end of file while reading trace file"));
 }
 
+/* Open the tfile target.  */
+
 static void
-tfile_open (const char *arg, int from_tty)
+tfile_target_open (const char *arg, int from_tty)
 {
-  char *temp;
-  struct cleanup *old_chain;
   int flags;
   int scratch_chan;
   char header[TRACE_HEADER_SIZE];
@@ -389,36 +464,31 @@ tfile_open (const char *arg, int from_tty)
   struct trace_status *ts;
   struct uploaded_tp *uploaded_tps = NULL;
   struct uploaded_tsv *uploaded_tsvs = NULL;
-  char *filename;
 
   target_preopen (from_tty);
   if (!arg)
     error (_("No trace file specified."));
 
-  filename = tilde_expand (arg);
-  if (!IS_ABSOLUTE_PATH(filename))
-    {
-      temp = concat (current_directory, "/", filename, (char *) NULL);
-      xfree (filename);
-      filename = temp;
-    }
-
-  old_chain = make_cleanup (xfree, filename);
+  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, flags, 0);
+  scratch_chan = gdb_open_cloexec (filename.get (), flags, 0);
   if (scratch_chan < 0)
-    perror_with_name (filename);
+    perror_with_name (filename.get ());
 
   /* 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);
 
-  trace_filename = xstrdup (filename);
+  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);
@@ -442,7 +512,7 @@ tfile_open (const char *arg, int from_tty)
   ts->disconnected_tracing = 0;
   ts->circular_buffer = 0;
 
-  TRY
+  try
     {
       /* Read through a section of newline-terminated lines that
         define things like tracepoints.  */
@@ -467,6 +537,9 @@ tfile_open (const char *arg, int from_tty)
            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;
 
@@ -475,17 +548,16 @@ tfile_open (const char *arg, int from_tty)
       if (trace_regblock_size == 0)
        error (_("No register block size recorded in trace file"));
     }
-  CATCH (ex, RETURN_MASK_ALL)
+  catch (const gdb_exception &ex)
     {
       /* Remove the partially set up target.  */
       unpush_target (&tfile_ops);
-      throw_exception (ex);
+      throw;
     }
-  END_CATCH
 
   inferior_appeared (current_inferior (), TFILE_PID);
-  inferior_ptid = pid_to_ptid (TFILE_PID);
-  add_thread_silent (inferior_ptid);
+  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."));
@@ -530,41 +602,42 @@ tfile_interp_line (char *line, struct uploaded_tp **utpp,
       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.  */
 
-static void
-tfile_close (struct target_ops *self)
+void
+tfile_target::close ()
 {
-  int pid;
+  gdb_assert (trace_fd != -1);
 
-  if (trace_fd < 0)
-    return;
-
-  pid = ptid_get_pid (inferior_ptid);
   inferior_ptid = null_ptid;   /* Avoid confusion from thread stuff.  */
-  exit_inferior_silent (pid);
+  exit_inferior_silent (current_inferior ());
 
-  close (trace_fd);
+  ::close (trace_fd);
   trace_fd = -1;
   xfree (trace_filename);
   trace_filename = NULL;
+  buffer_free (&trace_tdesc);
 
   trace_reset_local_state ();
 }
 
-static void
-tfile_files_info (struct target_ops *t)
+void
+tfile_target::files_info ()
 {
   printf_filtered ("\t`%s'\n", trace_filename);
 }
 
-static void
-tfile_get_tracepoint_status (struct target_ops *self,
-                            struct breakpoint *tp, struct uploaded_tp *utp)
+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.  */
@@ -594,8 +667,8 @@ tfile_get_traceframe_address (off_t tframe_offset)
 
   tp = get_tracepoint_by_number_on_target (tpnum);
   /* FIXME this is a poor heuristic if multiple locations.  */
-  if (tp && tp->base.loc)
-    addr = tp->base.loc->address;
+  if (tp && tp->loc)
+    addr = tp->loc->address;
 
   /* Restore our seek position.  */
   cur_offset = saved_offset;
@@ -608,9 +681,9 @@ tfile_get_traceframe_address (off_t tframe_offset)
    both the traceframe and tracepoint number, otherwise -1 for
    each.  */
 
-static int
-tfile_trace_find (struct target_ops *self, enum trace_find_type type, int num,
-                 CORE_ADDR addr1, CORE_ADDR addr2, int *tpp)
+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;
@@ -791,12 +864,11 @@ traceframe_find_block_type (char type_wanted, int pos)
 /* 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)
+void
+tfile_target::fetch_registers (struct regcache *regcache, int regno)
 {
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  int offset, regn, regsize;
+  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.  */
@@ -809,28 +881,28 @@ tfile_fetch_registers (struct target_ops *ops,
 
       tfile_read (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++)
        {
+         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_register_status (regcache, regn) == REG_UNKNOWN)
+         if (regcache->get_register_status (regn) == REG_UNKNOWN)
            {
              if (regno == regn)
                {
-                 regcache_raw_supply (regcache, regno, regs + offset);
+                 regcache->raw_supply (regno, regs + offset);
                  break;
                }
              else if (regno == -1)
                {
-                 regcache_raw_supply (regcache, regn, regs + offset);
+                 regcache->raw_supply (regn, regs + offset);
                }
            }
-         offset += regsize;
        }
     }
   else
@@ -838,12 +910,42 @@ tfile_fetch_registers (struct target_ops *ops,
 }
 
 static enum target_xfer_status
-tfile_xfer_partial (struct target_ops *ops, enum target_object object,
-                   const char *annex, gdb_byte *readbuf,
-                   const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
-                   ULONGEST *xfered_len)
+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 for now.  */
+  /* 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;
 
@@ -902,7 +1004,7 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
         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 = min (len, low_addr_available - offset);
+       len = std::min (len, low_addr_available - offset);
       res = exec_read_partial_read_only (readbuf, offset, len, xfered_len);
 
       if (res == TARGET_XFER_OK)
@@ -926,12 +1028,11 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
 /* 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 (struct target_ops *self,
-                                     int tsvnum, LONGEST *val)
+bool
+tfile_target::get_trace_state_variable_value (int tsvnum, LONGEST *val)
 {
   int pos;
-  int found = 0;
+  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
@@ -952,7 +1053,7 @@ tfile_get_trace_state_variable_value (struct target_ops *self,
          *val = extract_signed_integer ((gdb_byte *) val, 8,
                                         gdbarch_byte_order
                                         (target_gdbarch ()));
-         found = 1;
+         found = true;
        }
       pos += (4 + 8);
     }
@@ -972,7 +1073,6 @@ build_traceframe_info (char blocktype, void *data)
     {
     case 'M':
       {
-       struct mem_range *r;
        ULONGEST maddr;
        unsigned short mlen;
 
@@ -986,10 +1086,7 @@ build_traceframe_info (char blocktype, void *data)
                                          2, gdbarch_byte_order
                                          (target_gdbarch ()));
 
-       r = VEC_safe_push (mem_range_s, info->memory, NULL);
-
-       r->start = maddr;
-       r->length = mlen;
+       info->memory.emplace_back (maddr, mlen);
        break;
       }
     case 'V':
@@ -997,7 +1094,7 @@ build_traceframe_info (char blocktype, void *data)
        int vnum;
 
        tfile_read ((gdb_byte *) &vnum, 4);
-       VEC_safe_push (int, info->tvars, vnum);
+       info->tvars.push_back (vnum);
       }
     case 'R':
     case 'S':
@@ -1014,42 +1111,28 @@ build_traceframe_info (char blocktype, void *data)
   return 0;
 }
 
-static struct traceframe_info *
-tfile_traceframe_info (struct target_ops *self)
+traceframe_info_up
+tfile_target::traceframe_info ()
 {
-  struct traceframe_info *info = XCNEW (struct traceframe_info);
+  traceframe_info_up info (new struct traceframe_info);
+
+  traceframe_walk_blocks (build_traceframe_info, 0, info.get ());
 
-  traceframe_walk_blocks (build_traceframe_info, 0, info);
   return info;
 }
 
+/* Handles tdesc lines from tfile by appending the payload to
+   a global trace_tdesc variable.  */
+
 static void
-init_tfile_ops (void)
+tfile_append_tdesc_line (const char *line)
 {
-  init_tracefile_ops (&tfile_ops);
-
-  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_tracepoint_status = tfile_get_tracepoint_status;
-  tfile_ops.to_trace_find = tfile_trace_find;
-  tfile_ops.to_get_trace_state_variable_value
-    = tfile_get_trace_state_variable_value;
-  tfile_ops.to_traceframe_info = tfile_traceframe_info;
+  buffer_grow_str (&trace_tdesc, line);
+  buffer_grow_str (&trace_tdesc, "\n");
 }
 
-extern initialize_file_ftype _initialize_tracefile_tfile;
-
 void
 _initialize_tracefile_tfile (void)
 {
-  init_tfile_ops ();
-
-  add_target_with_completer (&tfile_ops, filename_completer);
+  add_target (tfile_target_info, tfile_target_open, filename_completer);
 }
This page took 0.034366 seconds and 4 git commands to generate.