btrace: Fix memory leak in btrace_clear.
[deliverable/binutils-gdb.git] / gdb / tracefile-tfile.c
index efa69b260d1fe3f4cdd69d4477aa3356c4ad9593..255bbc9ef96466abf75a5d3f4e506198bd0956ef 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
@@ -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,9 +420,8 @@ 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;
@@ -390,12 +433,13 @@ tfile_open (char *filename, 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 (!filename)
+  if (!arg)
     error (_("No trace file specified."));
 
-  filename = tilde_expand (filename);
+  filename = tilde_expand (arg);
   if (!IS_ABSOLUTE_PATH(filename))
     {
       temp = concat (current_directory, "/", filename, (char *) NULL);
@@ -419,13 +463,16 @@ tfile_open (char *filename, int from_tty)
   trace_filename = xstrdup (filename);
   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 +489,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 +514,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 +525,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 +560,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 +607,7 @@ tfile_close (struct target_ops *self)
   trace_fd = -1;
   xfree (trace_filename);
   trace_filename = NULL;
+  buffer_free (&trace_tdesc);
 
   trace_reset_local_state ();
 }
@@ -712,7 +769,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;
@@ -795,7 +852,7 @@ tfile_fetch_registers (struct target_ops *ops,
                       struct regcache *regcache, int regno)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  int offset, regn, regsize;
+  int offset, regn, regsize, dummy;
 
   /* An uninitialized reg size says we're not going to be
      successful at getting register blocks.  */
@@ -804,18 +861,19 @@ tfile_fetch_registers (struct target_ops *ops,
 
   if (traceframe_find_block_type ('R', 0) >= 0)
     {
-      gdb_byte *regs = alloca (trace_regblock_size);
+      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 (get_regcache_arch (regcache),
+                                                 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)
            {
@@ -829,20 +887,49 @@ tfile_fetch_registers (struct target_ops *ops,
                  regcache_raw_supply (regcache, regn, regs + offset);
                }
            }
-         offset += regsize;
        }
     }
   else
     tracefile_fetch_registers (regcache, regno);
 }
 
+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 (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;
+}
+
 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)
 {
-  /* 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;
 
@@ -853,6 +940,9 @@ tfile_xfer_partial (struct target_ops *ops, enum target_object object,
     {
       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.  */
@@ -886,13 +976,19 @@ 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);
        }
 
       /* Requested memory is unavailable in the context of traceframes,
         and this address falls within a read-only section, fallback
-        to reading from executable.  */
+        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)
@@ -956,7 +1052,7 @@ tfile_get_trace_state_variable_value (struct target_ops *self,
 static int
 build_traceframe_info (char blocktype, void *data)
 {
-  struct traceframe_info *info = data;
+  struct traceframe_info *info = (struct traceframe_info *) data;
 
   switch (blocktype)
     {
@@ -1013,6 +1109,16 @@ tfile_traceframe_info (struct target_ops *self)
   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)
 {
This page took 0.035942 seconds and 4 git commands to generate.