[sysprof/wip/chergert/mmap-writer] incremental work on mmap writers



commit cce4f8ed51bff8ed9aaef41ed052ea42641a539b
Author: Christian Hergert <chergert redhat com>
Date:   Tue Feb 11 07:39:07 2020 -0800

    incremental work on mmap writers

 src/libsysprof-capture/mmap-writer.c            | 144 +++++++++-
 src/libsysprof-capture/mmap-writer.h            |  32 ++-
 src/libsysprof-capture/sysprof-capture-reader.c |  51 ++--
 src/libsysprof-capture/sysprof-capture-reader.h |   3 +-
 src/libsysprof-capture/sysprof-capture-writer.c | 366 +++++-------------------
 src/libsysprof-capture/sysprof-capture-writer.h |   4 -
 src/tests/test-capture.c                        |  15 +-
 7 files changed, 269 insertions(+), 346 deletions(-)
---
diff --git a/src/libsysprof-capture/mmap-writer.c b/src/libsysprof-capture/mmap-writer.c
index 21a413d..66a14d0 100644
--- a/src/libsysprof-capture/mmap-writer.c
+++ b/src/libsysprof-capture/mmap-writer.c
@@ -68,6 +68,8 @@
 
 #include "mmap-writer.h"
 
+#include "sysprof-capture-util-private.h"
+
 struct _MmapWriter
 {
   /* The file-descriptor for our underlying file that is mapped
@@ -85,6 +87,12 @@ struct _MmapWriter
    */
   void *page_map;
 
+  /* Because special information is often stored at the head of a
+   * file, we map the head page specially so that it is always available
+   * for quick access.
+   */
+  void *head_page;
+
   /* The first page that is mapped (the byte offset is calculated
    * from (page*page_size).
    */
@@ -97,6 +105,78 @@ struct _MmapWriter
   goffset wr_offset;
 };
 
+static void
+mmap_writer_unmap_head (MmapWriter *self)
+{
+  g_assert (self != NULL);
+  g_assert (self->n_pages > 0);
+  g_assert (self->page_size > 0);
+
+  if (self->head_page != NULL)
+    {
+      msync (self->head_page, self->page_size, MS_SYNC);
+      madvise (self->head_page, self->page_size * self->n_pages, MADV_DONTNEED);
+      munmap (self->head_page, self->page_size);
+      self->head_page = NULL;
+    }
+}
+
+static gboolean
+mmap_writer_map_head (MmapWriter *self)
+{
+  void *map;
+
+  g_assert (self != NULL);
+  g_assert (self->head_page == NULL);
+  g_assert (self->n_pages > 0);
+  g_assert (self->page_size > 0);
+  g_assert (self->fd > -1);
+
+  if (posix_fallocate (self->fd, 0, self->page_size) < 0)
+    return FALSE;
+
+  map = mmap (NULL, self->page_size, PROT_WRITE | PROT_READ, MAP_SHARED, self->fd, 0);
+  if (map == MAP_FAILED)
+    return FALSE;
+
+  madvise (map, self->page_size * self->n_pages, MADV_WILLNEED);
+
+  self->head_page = map;
+
+  return TRUE;
+}
+
+static void
+mmap_writer_flush_page_map (MmapWriter *self)
+{
+  g_assert (self != NULL);
+
+  g_print ("Flushing %p %ld pages\n", self->page_map, self->n_pages);
+
+  if (self->page_map != NULL)
+    {
+      gsize wr_offset = self->wr_offset;
+      gsize n_pages = 0;
+
+      g_print ("Wr offset: %ld\n", self->wr_offset);
+
+      while (wr_offset > self->page_size)
+        {
+          wr_offset -= self->page_size;
+          n_pages++;
+        }
+
+      if (wr_offset > 0)
+        n_pages++;
+
+      g_print ("%ld pages\n", n_pages);
+
+      msync (self->page_map, n_pages * self->page_size, MS_SYNC);
+
+      g_print ("done\n");
+    }
+}
+
 static void
 mmap_writer_unmap (MmapWriter *self)
 {
@@ -106,6 +186,8 @@ mmap_writer_unmap (MmapWriter *self)
 
   if (self->page_map != NULL)
     {
+      g_print ("Unmapping %p\n", self->page_map);
+      mmap_writer_flush_page_map (self);
       madvise (self->page_map, self->page_size * self->n_pages, MADV_DONTNEED);
       munmap (self->page_map, self->page_size * self->n_pages);
       self->page_map = NULL;
@@ -123,20 +205,28 @@ mmap_writer_map (MmapWriter *self)
   g_assert (self->page_size > 0);
   g_assert (self->fd > -1);
 
+  if (posix_fallocate (self->fd,
+                       self->page * self->page_size,
+                       self->n_pages * self->page_size) < 0)
+    return FALSE;
+
   map = mmap (NULL,
               self->page_size * self->n_pages,
               PROT_WRITE | PROT_READ,
               MAP_SHARED,
               self->fd,
               self->page_size * self->page);
-
   if (map == MAP_FAILED)
     return FALSE;
 
-  madvise (map, self->page_size * self->n_pages, MADV_SEQUENTIAL);
+  madvise (map,
+           self->page_size * self->n_pages,
+           (MADV_NOHUGEPAGE | MADV_SEQUENTIAL));
 
   self->page_map = map;
 
+  g_print ("Map now at %p\n", map);
+
   return TRUE;
 }
 
@@ -146,6 +236,9 @@ normalize_buffer_size (MmapWriter *self,
 {
   g_assert (self != NULL);
   g_assert (self->page_map == NULL);
+  g_assert (self->page_size > 0);
+
+  g_print ("BUFFER SIZE: %ld\n", buffer_size);
 
   if (buffer_size == 0)
     {
@@ -163,6 +256,10 @@ normalize_buffer_size (MmapWriter *self,
 
   if (buffer_size > 0)
     self->n_pages++;
+
+  g_print ("N_PAGE: %ld\n", self->n_pages);
+
+  g_assert (self->n_pages > 0);
 }
 
 MmapWriter *
@@ -176,15 +273,15 @@ mmap_writer_new_for_fd (gint  fd,
 
   ret = g_atomic_rc_box_new0 (MmapWriter);
   ret->fd = fd;
-  ret->page_size = sysconf (_SC_PAGESIZE);
+  ret->page_size = _sysprof_getpagesize ();
   ret->page_map = NULL;
   ret->page = 0;
   ret->n_pages = 16;
-  ret->wr_offset = 0;
+  ret->wr_offset = sizeof (SysprofCaptureFileHeader);
 
   normalize_buffer_size (ret, buffer_size);
 
-  if (!mmap_writer_map (ret))
+  if (!mmap_writer_map (ret) || !mmap_writer_map_head (ret))
     {
       close (ret->fd);
       g_atomic_rc_box_release (ret);
@@ -214,8 +311,9 @@ mmap_writer_close (MmapWriter *self)
   g_assert (self != NULL);
 
   mmap_writer_unmap (self);
+  mmap_writer_unmap_head (self);
 
-  if (self->fd >= 0)
+  if (self->fd > -1)
     {
       close (self->fd);
       self->fd = -1;
@@ -225,9 +323,6 @@ mmap_writer_close (MmapWriter *self)
 void
 mmap_writer_destroy (MmapWriter *self)
 {
-  if (self == NULL)
-    return;
-
   g_atomic_rc_box_release_full (self, (GDestroyNotify)mmap_writer_close);
 }
 
@@ -280,8 +375,8 @@ mmap_writer_advance (MmapWriter *self,
         }
 
       /* Determine how many pages we need loaded */
-      req_pages = (length / self->page_size);
-      if ((req_pages & 0xFFF) != 0)
+      req_pages = (self->wr_offset + length) / self->page_size;
+      if (((self->wr_offset + length) % self->page_size) > 0)
         req_pages++;
 
       /* We might need to increase the buffer size */
@@ -328,5 +423,30 @@ mmap_writer_flush (MmapWriter *self)
 {
   g_assert (self != NULL);
 
-  fdatasync (self->fd);
+  if (self->head_page != NULL)
+    msync (self->head_page, self->page_size, MS_SYNC);
+
+  mmap_writer_flush_page_map (self);
+}
+
+SysprofCaptureFileHeader *
+mmap_writer_get_file_header (MmapWriter *self)
+{
+  g_assert (self != NULL);
+
+  return (SysprofCaptureFileHeader *)self->head_page;
+}
+
+gsize
+mmap_writer_get_buffer_size (MmapWriter *self)
+{
+  g_assert (self != NULL);
+
+  return self->n_pages * self->page_size;
+}
+
+goffset
+mmap_writer_tell (MmapWriter *self)
+{
+  return self->page * self->page_size + self->wr_offset;
 }
diff --git a/src/libsysprof-capture/mmap-writer.h b/src/libsysprof-capture/mmap-writer.h
index c54ea45..de84d99 100644
--- a/src/libsysprof-capture/mmap-writer.h
+++ b/src/libsysprof-capture/mmap-writer.h
@@ -56,31 +56,37 @@
 
 #pragma once
 
-#include <glib.h>
+#include "sysprof-capture-types.h"
 
 G_BEGIN_DECLS
 
 typedef struct _MmapWriter MmapWriter;
 
 G_GNUC_INTERNAL
-MmapWriter *mmap_writer_new        (const gchar *path,
-                                    gsize        buffer_size);
+MmapWriter               *mmap_writer_new             (const gchar *path,
+                                                       gsize        buffer_size);
 G_GNUC_INTERNAL
-MmapWriter *mmap_writer_new_for_fd (gint         fd,
-                                    gsize        buffer_size);
+MmapWriter               *mmap_writer_new_for_fd      (gint         fd,
+                                                       gsize        buffer_size);
 G_GNUC_INTERNAL
-void        mmap_writer_close      (MmapWriter  *self);
+void                      mmap_writer_close           (MmapWriter  *self);
 G_GNUC_INTERNAL
-void        mmap_writer_flush      (MmapWriter  *self);
+void                      mmap_writer_flush           (MmapWriter  *self);
 G_GNUC_INTERNAL
-void        mmap_writer_destroy    (MmapWriter  *self);
+void                      mmap_writer_destroy         (MmapWriter  *self);
 G_GNUC_INTERNAL
-gint        mmap_writer_get_fd     (MmapWriter  *self);
+gint                      mmap_writer_get_fd          (MmapWriter  *self);
 G_GNUC_INTERNAL
-gpointer    mmap_writer_advance    (MmapWriter  *self,
-                                    goffset      length);
+gpointer                  mmap_writer_advance         (MmapWriter  *self,
+                                                       goffset      length);
 G_GNUC_INTERNAL
-gpointer    mmap_writer_rewind     (MmapWriter  *self,
-                                    goffset      length);
+gpointer                  mmap_writer_rewind          (MmapWriter  *self,
+                                                       goffset      length);
+G_GNUC_INTERNAL
+SysprofCaptureFileHeader *mmap_writer_get_file_header (MmapWriter  *self);
+G_GNUC_INTERNAL
+gsize                     mmap_writer_get_buffer_size (MmapWriter  *self);
+G_GNUC_INTERNAL
+goffset                   mmap_writer_tell            (MmapWriter  *self);
 
 G_END_DECLS
diff --git a/src/libsysprof-capture/sysprof-capture-reader.c b/src/libsysprof-capture/sysprof-capture-reader.c
index 4774e3f..bd6823d 100644
--- a/src/libsysprof-capture/sysprof-capture-reader.c
+++ b/src/libsysprof-capture/sysprof-capture-reader.c
@@ -334,7 +334,20 @@ sysprof_capture_reader_ensure_space_for (SysprofCaptureReader *self,
 {
   g_assert (self != NULL);
   g_assert (self->pos <= self->len);
-  g_assert (len > 0);
+  g_assert (len >= sizeof (SysprofCaptureFrame));
+
+  /* If we have a writer that is loading data using mmap(), then there is a
+   * chance that we could hit zero section up to the end of the file. We can
+   * retry the buffered read though in that case so that we allow reading
+   * a file (without mmap) at the same time as writing with mmap. This is
+   * not really a common case except for in unit-testing.
+   */
+  if ((self->len - self->pos) >= sizeof (guint16) &&
+      *(guint16 *)(gpointer)&self->buf[self->pos] == 0)
+    {
+      self->fd_off -= (self->len - self->pos);
+      self->len = self->pos;
+    }
 
   if ((self->len - self->pos) < len)
     {
@@ -364,7 +377,17 @@ sysprof_capture_reader_ensure_space_for (SysprofCaptureReader *self,
         }
     }
 
-  return (self->len - self->pos) >= len;
+  if ((self->len - self->pos) >= len)
+    {
+      /* Make sure we got valid frame data back from the
+       * FD or else we might be in the zero-fill section
+       * up to the end of the file.
+       */
+      if (*(guint16 *)(gpointer)&self->buf[self->pos] >= len)
+        return TRUE;
+    }
+
+  return FALSE;
 }
 
 gboolean
@@ -419,6 +442,10 @@ sysprof_capture_reader_peek_frame (SysprofCaptureReader *self,
 
   sysprof_capture_reader_bswap_frame (self, frame);
 
+  /* In case the capture did not update the end_time during normal usage,
+   * we can update our cached known end_time based on the greatest frame
+   * we come across.
+   */
   if (frame->time > self->end_time)
     self->end_time = frame->time;
 
@@ -741,6 +768,7 @@ sysprof_capture_reader_read_jitmap (SysprofCaptureReader *self)
     return NULL;
 
   jitmap = (SysprofCaptureJitmap *)(gpointer)&self->buf[self->pos];
+  g_assert (jitmap->frame.len > 0);
 
   ret = g_hash_table_new_full (NULL, NULL, NULL, g_free);
 
@@ -961,26 +989,9 @@ sysprof_capture_reader_splice (SysprofCaptureReader  *self,
                                GError               **error)
 {
   g_assert (self != NULL);
-  g_assert (self->fd != -1);
   g_assert (dest != NULL);
 
-  /* Flush before writing anything to ensure consistency */
-  if (!sysprof_capture_writer_flush (dest))
-    {
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (errno),
-                   "%s", g_strerror (errno));
-      return FALSE;
-    }
-
-  /*
-   * We don't need to track position because writer will
-   * track the current position to avoid reseting it.
-   */
-
-  /* Perform the splice */
-  return _sysprof_capture_writer_splice_from_fd (dest, self->fd, error);
+  return sysprof_capture_writer_cat (dest, self, error);
 }
 
 /**
diff --git a/src/libsysprof-capture/sysprof-capture-reader.h b/src/libsysprof-capture/sysprof-capture-reader.h
index 3c58524..f8cf5e9 100644
--- a/src/libsysprof-capture/sysprof-capture-reader.h
+++ b/src/libsysprof-capture/sysprof-capture-reader.h
@@ -126,7 +126,8 @@ gboolean                            sysprof_capture_reader_reset               (
 SYSPROF_AVAILABLE_IN_ALL
 gboolean                            sysprof_capture_reader_splice              (SysprofCaptureReader      
*self,
                                                                                 SysprofCaptureWriter      
*dest,
-                                                                                GError                   
**error);
+                                                                                GError                   
**error)
+  G_GNUC_DEPRECATED_FOR (sysprof_capture_writer_cat);
 SYSPROF_AVAILABLE_IN_ALL
 gboolean                            sysprof_capture_reader_save_as             (SysprofCaptureReader      
*self,
                                                                                 const gchar               
*filename,
diff --git a/src/libsysprof-capture/sysprof-capture-writer.c b/src/libsysprof-capture/sysprof-capture-writer.c
index 5e206af..7e08124 100644
--- a/src/libsysprof-capture/sysprof-capture-writer.c
+++ b/src/libsysprof-capture/sysprof-capture-writer.c
@@ -71,6 +71,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "mmap-writer.h"
+
 #include "sysprof-capture-reader.h"
 #include "sysprof-capture-util-private.h"
 #include "sysprof-capture-writer.h"
@@ -91,48 +93,34 @@ typedef struct
 
 struct _SysprofCaptureWriter
 {
-  /*
-   * This is our buffer location for incoming strings. This is used
-   * similarly to GStringChunk except there is only one-page, and after
-   * it fills, we flush to disk.
+  /* Our hashtable for JIT deduplication.
    *
-   * This is paired with a closed hash table for deduplication.
-   */
-  gchar addr_buf[4096*4];
-
-  /* Our hashtable for deduplication. */
-  SysprofCaptureJitmapBucket addr_hash[512];
-
-  /* We keep the large fields above so that our allocation will be page
-   * alinged for the write buffer. This improves the performance of large
-   * writes to the target file-descriptor.
-   */
-  volatile gint ref_count;
-
-  /*
-   * Our address sequence counter. The value that comes from
+   * Our address sequence counter is the value that comes from
    * monotonically increasing this is OR'd with JITMAP_MARK to denote
    * the address name should come from the JIT map.
-   */
-  gsize addr_seq;
-
-  /* Our position in addr_buf. */
-  gsize addr_buf_pos;
-
-  /*
-   * The number of hash table items in @addr_hash. This is an
-   * optimization so that we can avoid calculating the number of strings
+   *
+   * The addr_hash_size is the number of items in @addr_hash. This is
+   * an optimization so that we avoid calculating the number of strings
    * when flushing out the jitmap.
    */
+  guint8 addr_buf[3584];
+  SysprofCaptureJitmapBucket addr_hash[512];
+  gsize addr_buf_pos;
+  gsize addr_seq;
   guint addr_hash_size;
 
-  /* Capture file handle */
-  int fd;
-
-  /* Our write buffer for fd */
-  guint8 *buf;
-  gsize pos;
-  gsize len;
+  /* The MmapWriter abstracts writing to a memory mapped file which is
+   * persisted to underlying storage. The goal here is to ensure that as
+   * we write data, it can be retrieved in case our program crashes. This
+   * is particularly useful when the external Sysprof profiler stops the
+   * profiler but the inferior process does not exit. The profiler can
+   * read up to the known data and then skip any unfinished data when
+   * muxing the streams.
+   *
+   * To make updating the SysprofCaptureFileHeader faster the MmapWriter
+   * keeps the head page around and available.
+   */
+  MmapWriter *writer;
 
   /* GSource for periodic flush */
   GSource *periodic_flush;
@@ -152,8 +140,6 @@ sysprof_capture_writer_frame_init (SysprofCaptureFrame     *frame_,
                                    gint64                   time_,
                                    SysprofCaptureFrameType  type)
 {
-  g_assert (frame_ != NULL);
-
   frame_->len = len;
   frame_->cpu = cpu;
   frame_->pid = pid;
@@ -169,74 +155,30 @@ sysprof_capture_writer_finalize (SysprofCaptureWriter *self)
   if (self != NULL)
     {
       g_clear_pointer (&self->periodic_flush, g_source_destroy);
-
       sysprof_capture_writer_flush (self);
-
-      if (self->fd != -1)
-        {
-          close (self->fd);
-          self->fd = -1;
-        }
-
-      g_free (self->buf);
-      g_free (self);
+      g_clear_pointer (&self->writer, mmap_writer_destroy);
     }
 }
 
 SysprofCaptureWriter *
 sysprof_capture_writer_ref (SysprofCaptureWriter *self)
 {
-  g_assert (self != NULL);
-  g_assert (self->ref_count > 0);
-
-  g_atomic_int_inc (&self->ref_count);
-
-  return self;
+  return g_atomic_rc_box_acquire (self);
 }
 
 void
 sysprof_capture_writer_unref (SysprofCaptureWriter *self)
 {
-  g_assert (self != NULL);
-  g_assert (self->ref_count > 0);
-
-  if (g_atomic_int_dec_and_test (&self->ref_count))
-    sysprof_capture_writer_finalize (self);
+  g_atomic_rc_box_release_full (self, (GDestroyNotify)sysprof_capture_writer_finalize);
 }
 
 static gboolean
 sysprof_capture_writer_flush_data (SysprofCaptureWriter *self)
 {
-  const guint8 *buf;
-  gssize written;
-  gsize to_write;
-
   g_assert (self != NULL);
-  g_assert (self->pos <= self->len);
-  g_assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
-
-  if (self->pos == 0)
-    return TRUE;
 
-  buf = self->buf;
-  to_write = self->pos;
-
-  while (to_write > 0)
-    {
-      written = _sysprof_write (self->fd, buf, to_write);
-      if (written < 0)
-        return FALSE;
-
-      if (written == 0 && errno != EAGAIN)
-        return FALSE;
-
-      g_assert (written <= (gssize)to_write);
-
-      buf += written;
-      to_write -= written;
-    }
-
-  self->pos = 0;
+  if (self->writer != NULL)
+    mmap_writer_flush (self->writer);
 
   return TRUE;
 }
@@ -247,52 +189,23 @@ sysprof_capture_writer_realign (gsize *pos)
   *pos = (*pos + SYSPROF_CAPTURE_ALIGN - 1) & ~(SYSPROF_CAPTURE_ALIGN - 1);
 }
 
-static inline gboolean
-sysprof_capture_writer_ensure_space_for (SysprofCaptureWriter *self,
-                                         gsize                 len)
-{
-  /* Check for max frame size */
-  if (len > G_MAXUSHORT)
-    return FALSE;
-
-  if ((self->len - self->pos) < len)
-    {
-      if (!sysprof_capture_writer_flush_data (self))
-        return FALSE;
-    }
-
-  return TRUE;
-}
-
 static inline gpointer
 sysprof_capture_writer_allocate (SysprofCaptureWriter *self,
                                  gsize                *len)
 {
-  gpointer p;
-
   g_assert (self != NULL);
   g_assert (len != NULL);
-  g_assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
+  g_assert (self->writer != NULL);
 
   sysprof_capture_writer_realign (len);
 
-  if (!sysprof_capture_writer_ensure_space_for (self, *len))
-    return NULL;
-
-  p = (gpointer)&self->buf[self->pos];
-
-  self->pos += *len;
-
-  g_assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
-
-  return p;
+  return mmap_writer_advance (self->writer, *len);
 }
 
 static gboolean
 sysprof_capture_writer_flush_jitmap (SysprofCaptureWriter *self)
 {
-  SysprofCaptureJitmap jitmap;
-  gssize r;
+  SysprofCaptureJitmap *jitmap;
   gsize len;
 
   g_assert (self != NULL);
@@ -302,24 +215,20 @@ sysprof_capture_writer_flush_jitmap (SysprofCaptureWriter *self)
 
   g_assert (self->addr_buf_pos > 0);
 
-  len = sizeof jitmap + self->addr_buf_pos;
-
+  len = sizeof *jitmap + self->addr_buf_pos;
   sysprof_capture_writer_realign (&len);
 
-  sysprof_capture_writer_frame_init (&jitmap.frame,
+  jitmap = mmap_writer_advance (self->writer, len);
+  sysprof_capture_writer_frame_init (&jitmap->frame,
                                      len,
                                      -1,
                                      _sysprof_getpid (),
                                      SYSPROF_CAPTURE_CURRENT_TIME,
                                      SYSPROF_CAPTURE_FRAME_JITMAP);
-  jitmap.n_jitmaps = self->addr_hash_size;
+  jitmap->n_jitmaps = self->addr_hash_size;
 
-  if (sizeof jitmap != _sysprof_write (self->fd, &jitmap, sizeof jitmap))
-    return FALSE;
-
-  r = _sysprof_write (self->fd, self->addr_buf, len - sizeof jitmap);
-  if (r < 0 || (gsize)r != (len - sizeof jitmap))
-    return FALSE;
+  /* Copy the jitmap buffer into the frame data */
+  memcpy (jitmap->data, self->addr_buf, self->addr_buf_pos);
 
   self->addr_buf_pos = 0;
   self->addr_hash_size = 0;
@@ -387,7 +296,6 @@ sysprof_capture_writer_insert_jitmap (SysprofCaptureWriter *self,
 
   g_assert (self != NULL);
   g_assert (str != NULL);
-  g_assert ((self->pos % SYSPROF_CAPTURE_ALIGN) == 0);
 
   len = sizeof addr + strlen (str) + 1;
 
@@ -466,7 +374,6 @@ sysprof_capture_writer_new_from_fd (int   fd,
   g_autoptr(GDateTime) now = NULL;
   SysprofCaptureWriter *self;
   SysprofCaptureFileHeader *header;
-  gsize header_len = sizeof(*header);
 
   if (fd < 0)
     return NULL;
@@ -480,19 +387,14 @@ sysprof_capture_writer_new_from_fd (int   fd,
   /* This is only useful on files, memfd, etc */
   if (ftruncate (fd, 0) != 0) { /* Do Nothing */ }
 
-  self = g_new0 (SysprofCaptureWriter, 1);
-  self->ref_count = 1;
-  self->fd = fd;
-  self->buf = (guint8 *)g_malloc0 (buffer_size);
-  self->len = buffer_size;
+  self = g_atomic_rc_box_new0 (SysprofCaptureWriter);
+  self->writer = mmap_writer_new_for_fd (fd, buffer_size);
   self->next_counter_id = 1;
 
   now = g_date_time_new_now_local ();
   nowstr = g_date_time_format_iso8601 (now);
 
-  header = sysprof_capture_writer_allocate (self, &header_len);
-
-  if (header == NULL)
+  if (!(header = mmap_writer_get_file_header (self->writer)))
     {
       sysprof_capture_writer_finalize (self);
       return NULL;
@@ -511,18 +413,7 @@ sysprof_capture_writer_new_from_fd (int   fd,
   header->end_time = 0;
   memset (header->suffix, 0, sizeof header->suffix);
 
-  if (!sysprof_capture_writer_flush_data (self))
-    {
-      sysprof_capture_writer_finalize (self);
-      return NULL;
-    }
-
-  g_assert (self->pos == 0);
-  g_assert (self->len > 0);
-  g_assert (self->len % _sysprof_getpagesize() == 0);
-  g_assert (self->buf != NULL);
-  g_assert (self->addr_hash_size == 0);
-  g_assert (self->fd != -1);
+  mmap_writer_flush (self->writer);
 
   return self;
 }
@@ -860,21 +751,12 @@ sysprof_capture_writer_add_timestamp (SysprofCaptureWriter *self,
 static gboolean
 sysprof_capture_writer_flush_end_time (SysprofCaptureWriter *self)
 {
-  gint64 end_time = SYSPROF_CAPTURE_CURRENT_TIME;
-  ssize_t ret;
+  SysprofCaptureFileHeader *header;
 
   g_assert (self != NULL);
 
-  /* This field is opportunistic, so a failure is okay. */
-
-again:
-  ret = _sysprof_pwrite (self->fd,
-                         &end_time,
-                         sizeof (end_time),
-                         G_STRUCT_OFFSET (SysprofCaptureFileHeader, end_time));
-
-  if (ret < 0 && errno == EAGAIN)
-    goto again;
+  header = mmap_writer_get_file_header (self->writer);
+  header->end_time = SYSPROF_CAPTURE_CURRENT_TIME;
 
   return TRUE;
 }
@@ -910,10 +792,11 @@ sysprof_capture_writer_save_as (SysprofCaptureWriter  *self,
   gsize to_write;
   off_t in_off;
   off_t pos;
+  int writer_fd;
   int fd = -1;
 
   g_assert (self != NULL);
-  g_assert (self->fd != -1);
+  g_assert (self->writer != NULL);
   g_assert (filename != NULL);
 
   if (-1 == (fd = open (filename, O_CREAT | O_RDWR, 0640)))
@@ -922,8 +805,8 @@ sysprof_capture_writer_save_as (SysprofCaptureWriter  *self,
   if (!sysprof_capture_writer_flush (self))
     goto handle_errno;
 
-  if (-1 == (pos = lseek (self->fd, 0L, SEEK_CUR)))
-    goto handle_errno;
+  writer_fd = mmap_writer_get_fd (self->writer);
+  pos = mmap_writer_tell (self->writer);
 
   to_write = pos;
   in_off = 0;
@@ -932,7 +815,7 @@ sysprof_capture_writer_save_as (SysprofCaptureWriter  *self,
     {
       gssize written;
 
-      written = _sysprof_sendfile (fd, self->fd, &in_off, pos);
+      written = _sysprof_sendfile (fd, writer_fd, &in_off, to_write);
 
       if (written < 0)
         goto handle_errno;
@@ -964,77 +847,6 @@ handle_errno:
   return FALSE;
 }
 
-/**
- * _sysprof_capture_writer_splice_from_fd:
- * @self: An #SysprofCaptureWriter
- * @fd: the fd to read from.
- * @error: A location for a #GError, or %NULL.
- *
- * This is internal API for SysprofCaptureWriter and SysprofCaptureReader to
- * communicate when splicing a reader into a writer.
- *
- * This should not be used outside of #SysprofCaptureReader or
- * #SysprofCaptureWriter.
- *
- * This will not advance the position of @fd.
- *
- * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
- */
-gboolean
-_sysprof_capture_writer_splice_from_fd (SysprofCaptureWriter  *self,
-                                        int                    fd,
-                                        GError               **error)
-{
-  struct stat stbuf;
-  off_t in_off;
-  gsize to_write;
-
-  g_assert (self != NULL);
-  g_assert (self->fd != -1);
-
-  if (-1 == fstat (fd, &stbuf))
-    goto handle_errno;
-
-  if (stbuf.st_size < 256)
-    {
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   G_FILE_ERROR_INVAL,
-                   "Cannot splice, possibly corrupt file.");
-      return FALSE;
-    }
-
-  in_off = 256;
-  to_write = stbuf.st_size - in_off;
-
-  while (to_write > 0)
-    {
-      gssize written;
-
-      written = _sysprof_sendfile (self->fd, fd, &in_off, to_write);
-
-      if (written < 0)
-        goto handle_errno;
-
-      if (written == 0 && errno != EAGAIN)
-        goto handle_errno;
-
-      g_assert (written <= (gssize)to_write);
-
-      to_write -= written;
-    }
-
-  return TRUE;
-
-handle_errno:
-  g_set_error (error,
-               G_FILE_ERROR,
-               g_file_error_from_errno (errno),
-               "%s", g_strerror (errno));
-
-  return FALSE;
-}
-
 /**
  * sysprof_capture_writer_splice:
  * @self: An #SysprofCaptureWriter
@@ -1053,41 +865,20 @@ sysprof_capture_writer_splice (SysprofCaptureWriter  *self,
                                SysprofCaptureWriter  *dest,
                                GError               **error)
 {
+  SysprofCaptureReader *reader;
   gboolean ret;
-  off_t pos;
 
   g_assert (self != NULL);
-  g_assert (self->fd != -1);
   g_assert (dest != NULL);
-  g_assert (dest->fd != -1);
-
-  /* Flush before writing anything to ensure consistency */
-  if (!sysprof_capture_writer_flush (self) || !sysprof_capture_writer_flush (dest))
-    goto handle_errno;
 
-  /* Track our current position so we can reset */
-  if ((off_t)-1 == (pos = lseek (self->fd, 0L, SEEK_CUR)))
-    goto handle_errno;
+  if (!(reader = sysprof_capture_writer_create_reader (self, error)))
+    return FALSE;
 
-  /* Perform the splice */
-  ret = _sysprof_capture_writer_splice_from_fd (dest, self->fd, error);
+  ret = sysprof_capture_writer_cat (dest, reader, error);
 
-  /* Now reset or file-descriptor position (it should be the same */
-  if (pos != lseek (self->fd, pos, SEEK_SET))
-    {
-      ret = FALSE;
-      goto handle_errno;
-    }
+  sysprof_capture_reader_unref (reader);
 
   return ret;
-
-handle_errno:
-  g_set_error (error,
-               G_FILE_ERROR,
-               g_file_error_from_errno (errno),
-               "%s", g_strerror (errno));
-
-  return FALSE;
 }
 
 /**
@@ -1109,25 +900,18 @@ sysprof_capture_writer_create_reader (SysprofCaptureWriter  *self,
                                       GError               **error)
 {
   SysprofCaptureReader *ret;
+  int fd;
   int copy;
 
   g_return_val_if_fail (self != NULL, NULL);
-  g_return_val_if_fail (self->fd != -1, NULL);
 
-  if (!sysprof_capture_writer_flush (self))
-    {
-      g_set_error (error,
-                   G_FILE_ERROR,
-                   g_file_error_from_errno (errno),
-                   "%s", g_strerror (errno));
-      return NULL;
-    }
+  fd = mmap_writer_get_fd (self->writer);
 
   /*
    * We don't care about the write position, since the reader
    * uses positioned reads.
    */
-  if (-1 == (copy = dup (self->fd)))
+  if (-1 == (copy = dup (fd)))
     return NULL;
 
   if ((ret = sysprof_capture_reader_new_from_fd (copy, error)))
@@ -1304,27 +1088,15 @@ _sysprof_capture_writer_set_time_range (SysprofCaptureWriter *self,
                                         gint64                start_time,
                                         gint64                end_time)
 {
-  ssize_t ret;
+  SysprofCaptureFileHeader *header;
 
   g_assert (self != NULL);
 
-do_start:
-  ret = _sysprof_pwrite (self->fd,
-                         &start_time,
-                         sizeof (start_time),
-                         G_STRUCT_OFFSET (SysprofCaptureFileHeader, time));
-
-  if (ret < 0 && errno == EAGAIN)
-    goto do_start;
-
-do_end:
-  ret = _sysprof_pwrite (self->fd,
-                         &end_time,
-                         sizeof (end_time),
-                         G_STRUCT_OFFSET (SysprofCaptureFileHeader, end_time));
-
-  if (ret < 0 && errno == EAGAIN)
-    goto do_end;
+  if ((header = mmap_writer_get_file_header (self->writer)))
+    {
+      header->time = start_time;
+      header->end_time = end_time;
+    }
 
   return TRUE;
 }
@@ -1356,7 +1128,7 @@ sysprof_capture_writer_get_buffer_size (SysprofCaptureWriter *self)
 {
   g_return_val_if_fail (self != NULL, 0);
 
-  return self->len;
+  return mmap_writer_get_buffer_size (self->writer);
 }
 
 gboolean
@@ -1557,8 +1329,14 @@ sysprof_capture_writer_add_allocation (SysprofCaptureWriter  *self,
     {
       gsize diff = (sizeof (SysprofCaptureAddress) * (MAX_UNWIND_DEPTH - ev->n_addrs));
 
-      ev->frame.len -= diff;
-      self->pos -= diff;
+      /* We can simply rewind the length to what we used here because
+       * SYSPROF_CAPTURE_ALIGN matches sizeof(SysprofCaptureAddress).
+       */
+      if (diff > 0)
+        {
+          ev->frame.len -= diff;
+          mmap_writer_rewind (self->writer, diff);
+        }
     }
 
   self->stat.frame_count[SYSPROF_CAPTURE_FRAME_ALLOCATION]++;
diff --git a/src/libsysprof-capture/sysprof-capture-writer.h b/src/libsysprof-capture/sysprof-capture-writer.h
index 657e726..4eeb18b 100644
--- a/src/libsysprof-capture/sysprof-capture-writer.h
+++ b/src/libsysprof-capture/sysprof-capture-writer.h
@@ -237,10 +237,6 @@ gboolean              sysprof_capture_writer_cat                             (Sy
                                                                               SysprofCaptureReader           
   *reader,
                                                                               GError                         
  **error);
 G_GNUC_INTERNAL
-gboolean              _sysprof_capture_writer_splice_from_fd                 (SysprofCaptureWriter           
   *self,
-                                                                              int                            
    fd,
-                                                                              GError                         
  **error) G_GNUC_INTERNAL;
-G_GNUC_INTERNAL
 gboolean              _sysprof_capture_writer_set_time_range                 (SysprofCaptureWriter           
   *self,
                                                                               gint64                         
    start_time,
                                                                               gint64                         
    end_time) G_GNUC_INTERNAL;
diff --git a/src/tests/test-capture.c b/src/tests/test-capture.c
index 64d6f2c..fe9c6f8 100644
--- a/src/tests/test-capture.c
+++ b/src/tests/test-capture.c
@@ -239,7 +239,7 @@ test_reader_basic (void)
     const SysprofCaptureCounterDefine *def;
 
     def = sysprof_capture_reader_read_counter_define (reader);
-    g_assert (def != NULL);
+    g_assert_nonnull (def);
     g_assert_cmpint (def->n_counters, ==, 10);
 
     for (i = 0; i < def->n_counters; i++)
@@ -421,7 +421,16 @@ test_reader_basic (void)
     g_assert_cmpint (buf1len, >, 0);
     g_assert_cmpint (buf2len, >, 0);
 
-    g_assert_cmpint (buf1len, ==, buf2len);
+    /* Make sure the sizes match or else there is only zero padding
+     * at the end of our capture file that is truncated from the
+     * backup file.
+     */
+    if (buf1len != buf2len)
+      {
+        for (gsize j = buf1len; j < buf2len; j++)
+          g_assert_cmpint (buf2[j], ==, 0);
+      }
+
     g_assert_true (0 == memcmp (buf1, buf2, buf1len));
   }
 
@@ -544,7 +553,9 @@ test_reader_splice (void)
   r = sysprof_capture_reader_peek_type (reader, &type);
   g_assert_cmpint (r, ==, FALSE);
 
+  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
   r = sysprof_capture_reader_splice (reader, writer2, &error);
+  G_GNUC_END_IGNORE_DEPRECATIONS
   g_assert_no_error (error);
   g_assert_cmpint (r, ==, TRUE);
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]