Add assembler and disassembler support for the new Armv8.4-a registers for AArch64.
[deliverable/binutils-gdb.git] / gdb / tracefile-tfile.c
index 2daa560f641049e8457076d37b9edc56f079d218..6ef904daeb5d0d99be4a242bd3ef3b0eeb9995e6 100644 (file)
@@ -1,6 +1,6 @@
 /* Trace file TFILE format support in GDB.
 
-   Copyright (C) 1997-2014 Free Software Foundation, Inc.
+   Copyright (C) 1997-2017 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "exec.h" /* exec_bfd */
 #include "completer.h"
 #include "filenames.h"
+#include "remote.h"
+#include "xml-tdesc.h"
+#include "target-descriptions.h"
+#include "buffer.h"
+#include <algorithm>
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
@@ -84,7 +89,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 +138,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 +192,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 +204,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);
@@ -262,6 +267,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_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 +356,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,
@@ -327,7 +369,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;
@@ -352,7 +394,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);
@@ -376,11 +420,9 @@ tfile_read (gdb_byte *readbuf, int size)
 }
 
 static void
-tfile_open (char *filename, int from_tty)
+tfile_open (const char *arg, int from_tty)
 {
-  volatile struct gdb_exception ex;
   char *temp;
-  struct cleanup *old_chain;
   int flags;
   int scratch_chan;
   char header[TRACE_HEADER_SIZE];
@@ -392,40 +434,37 @@ tfile_open (char *filename, int from_tty)
   struct uploaded_tsv *uploaded_tsvs = NULL;
 
   target_preopen (from_tty);
-  if (!filename)
+  if (!arg)
     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);
+  gdb::unique_xmalloc_ptr<char> filename (tilde_expand (arg));
+  if (!IS_ABSOLUTE_PATH (filename.get ()))
+    filename.reset (concat (current_directory, "/", filename.get (),
+                           (char *) NULL));
 
   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);
 
   bytes += TRACE_HEADER_SIZE;
   if (!(header[0] == 0x7f
-       && (strncmp (header + 1, "TRACE0\n", 7) == 0)))
+       && (startswith (header + 1, "TRACE0\n"))))
     error (_("File is not a valid trace file."));
 
   push_target (&tfile_ops);
@@ -442,7 +481,7 @@ tfile_open (char *filename, int from_tty)
   ts->disconnected_tracing = 0;
   ts->circular_buffer = 0;
 
-  TRY_CATCH (ex, RETURN_MASK_ALL)
+  TRY
     {
       /* Read through a section of newline-terminated lines that
         define things like tracepoints.  */
@@ -467,6 +506,9 @@ tfile_open (char *filename, 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,12 +517,13 @@ tfile_open (char *filename, int from_tty)
       if (trace_regblock_size == 0)
        error (_("No register block size recorded in trace file"));
     }
-  if (ex.reason < 0)
+  CATCH (ex, RETURN_MASK_ALL)
     {
       /* Remove the partially set up target.  */
       unpush_target (&tfile_ops);
       throw_exception (ex);
     }
+  END_CATCH
 
   inferior_appeared (current_inferior (), TFILE_PID);
   inferior_ptid = pid_to_ptid (TFILE_PID);
@@ -509,26 +552,31 @@ tfile_interp_line (char *line, struct uploaded_tp **utpp,
 {
   char *p = line;
 
-  if (strncmp (p, "R ", strlen ("R ")) == 0)
+  if (startswith (p, "R "))
     {
       p += strlen ("R ");
       trace_regblock_size = strtol (p, &p, 16);
     }
-  else if (strncmp (p, "status ", strlen ("status ")) == 0)
+  else if (startswith (p, "status "))
     {
       p += strlen ("status ");
       parse_trace_status (p, current_trace_status ());
     }
-  else if (strncmp (p, "tp ", strlen ("tp ")) == 0)
+  else if (startswith (p, "tp "))
     {
       p += strlen ("tp ");
       parse_tracepoint_definition (p, utpp);
     }
-  else if (strncmp (p, "tsv ", strlen ("tsv ")) == 0)
+  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);
 }
@@ -551,6 +599,7 @@ tfile_close (struct target_ops *self)
   trace_fd = -1;
   xfree (trace_filename);
   trace_filename = NULL;
+  buffer_free (&trace_tdesc);
 
   trace_reset_local_state ();
 }
@@ -593,8 +642,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;
@@ -712,7 +761,7 @@ typedef int (*walk_blocks_callback_func) (char blocktype, void *data);
 static int
 match_blocktype (char blocktype, void *data)
 {
-  char *wantedp = data;
+  char *wantedp = (char *) data;
 
   if (*wantedp == blocktype)
     return 1;
@@ -794,29 +843,29 @@ static void
 tfile_fetch_registers (struct target_ops *ops,
                       struct regcache *regcache, int regno)
 {
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  int offset, regn, regsize, pc_regno;
-  gdb_byte *regs;
+  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;
 
-  regs = alloca (trace_regblock_size);
-
   if (traceframe_find_block_type ('R', 0) >= 0)
     {
+      gdb_byte *regs = (gdb_byte *) alloca (trace_regblock_size);
+
       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)
+         if (offset + regsize > trace_regblock_size)
            break;
          if (regcache_register_status (regcache, regn) == REG_UNKNOWN)
            {
@@ -830,58 +879,37 @@ tfile_fetch_registers (struct target_ops *ops,
                  regcache_raw_supply (regcache, regn, regs + offset);
                }
            }
-         offset += regsize;
        }
-      return;
     }
+  else
+    tracefile_fetch_registers (regcache, regno);
+}
 
-  /* We get here if no register data has been found.  Mark registers
-     as unavailable.  */
-  for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
-    regcache_raw_supply (regcache, regn, NULL);
-
-  /* We can often usefully guess that the PC is going to be the same
-     as the address of the tracepoint.  */
-  pc_regno = gdbarch_pc_regnum (gdbarch);
-
-  /* XXX This guessing code below only works if the PC register isn't
-     a pseudo-register.  The value of a pseudo-register isn't stored
-     in the (non-readonly) regcache -- instead it's recomputed
-     (probably from some other cached raw register) whenever the
-     register is read.  This guesswork should probably move to some
-     higher layer.  */
-  if (pc_regno < 0 || pc_regno >= gdbarch_num_regs (gdbarch))
-    return;
+static enum target_xfer_status
+tfile_xfer_partial_features (struct target_ops *ops, 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 (regno == -1 || regno == pc_regno)
-    {
-      struct tracepoint *tp = get_tracepoint (get_tracepoint_number ());
+  if (readbuf == NULL)
+    error (_("tfile_xfer_partial: tdesc is read-only"));
 
-      if (tp && tp->base.loc)
-       {
-         /* But don't try to guess if tracepoint is multi-location...  */
-         if (tp->base.loc->next)
-           {
-             warning (_("Tracepoint %d has multiple "
-                        "locations, cannot infer $pc"),
-                      tp->base.number);
-             return;
-           }
-         /* ... or does while-stepping.  */
-         if (tp->step_count > 0)
-           {
-             warning (_("Tracepoint %d does while-stepping, "
-                        "cannot infer $pc"),
-                      tp->base.number);
-             return;
-           }
+  if (trace_tdesc.used_size == 0)
+    return TARGET_XFER_E_IO;
 
-         store_unsigned_integer (regs, register_size (gdbarch, pc_regno),
-                                 gdbarch_byte_order (gdbarch),
-                                 tp->base.loc->address);
-         regcache_raw_supply (regcache, pc_regno, regs);
-       }
-    }
+  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;
 }
 
 static enum target_xfer_status
@@ -890,7 +918,10 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
                    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 (ops, annex, readbuf, writebuf,
+                                       offset, len, xfered_len);
   if (object != TARGET_OBJECT_MEMORY)
     return TARGET_XFER_E_IO;
 
@@ -900,6 +931,10 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
   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.  */
@@ -933,46 +968,37 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
              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);
        }
-    }
 
-  /* It's unduly pedantic to refuse to look at the executable for
-     read-only pieces; so do the equivalent of readonly regions aka
-     QTro packet.  */
-  /* FIXME account for relocation at some point.  */
-  if (exec_bfd)
-    {
-      asection *s;
-      bfd_size_type size;
-      bfd_vma vma;
+      /* 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);
 
-      for (s = exec_bfd->sections; s; s = s->next)
+      if (res == TARGET_XFER_OK)
+       return TARGET_XFER_OK;
+      else
        {
-         if ((s->flags & SEC_LOAD) == 0
-             || (s->flags & SEC_READONLY) == 0)
-           continue;
-
-         vma = s->vma;
-         size = bfd_get_section_size (s);
-         if (vma <= offset && offset < (vma + size))
-           {
-             ULONGEST amt;
-
-             amt = (vma + size) - offset;
-             if (amt > len)
-               amt = len;
-
-             *xfered_len = bfd_get_section_contents (exec_bfd, s,
-                                                     readbuf, offset - vma, amt);
-             return TARGET_XFER_OK;
-           }
+         /* No use trying further, we know some memory starting
+            at MEMADDR isn't available.  */
+         *xfered_len = len;
+         return TARGET_XFER_UNAVAILABLE;
        }
     }
-
-  /* Indicate failure to find the requested memory block.  */
-  return TARGET_XFER_E_IO;
+  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'
@@ -1012,31 +1038,18 @@ tfile_get_trace_state_variable_value (struct target_ops *self,
   return found;
 }
 
-static int
-tfile_has_all_memory (struct target_ops *ops)
-{
-  return 1;
-}
-
-static int
-tfile_has_memory (struct target_ops *ops)
-{
-  return 1;
-}
-
 /* 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 = data;
+  struct traceframe_info *info = (struct traceframe_info *) data;
 
   switch (blocktype)
     {
     case 'M':
       {
-       struct mem_range *r;
        ULONGEST maddr;
        unsigned short mlen;
 
@@ -1050,10 +1063,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':
@@ -1061,7 +1071,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':
@@ -1078,15 +1088,26 @@ build_traceframe_info (char blocktype, void *data)
   return 0;
 }
 
-static struct traceframe_info *
+static traceframe_info_up
 tfile_traceframe_info (struct target_ops *self)
 {
-  struct traceframe_info *info = XCNEW (struct traceframe_info);
+  traceframe_info_up info (new 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
+tfile_append_tdesc_line (const char *line)
+{
+  buffer_grow_str (&trace_tdesc, line);
+  buffer_grow_str (&trace_tdesc, "\n");
+}
+
 static void
 init_tfile_ops (void)
 {
@@ -1105,13 +1126,9 @@ init_tfile_ops (void)
   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_has_all_memory = tfile_has_all_memory;
-  tfile_ops.to_has_memory = tfile_has_memory;
   tfile_ops.to_traceframe_info = tfile_traceframe_info;
 }
 
-extern initialize_file_ftype _initialize_tracefile_tfile;
-
 void
 _initialize_tracefile_tfile (void)
 {
This page took 0.031813 seconds and 4 git commands to generate.