[sysprof/wip/chergert/mem-preload: 3/5] memory: add preload to write out to sysprof capture



commit bd52b2c0ef271385b8be619bf2dff52d9e343ee2
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jan 30 18:26:34 2020 -0800

    memory: add preload to write out to sysprof capture
    
    This is a very barebones implementation to do some capture recording
    of memory allocations. It only uses a single writer with a lock which
    appears to be fast enough for running a GTK app. However we may want
    to do a multi-threaded solution with TLS later and multiple writers.
    
    Still to do is the backtrace code to get instruction pointers for the
    current stack.

 src/libsysprof/meson.build                        |   2 +
 src/libsysprof/preload/meson.build                |  10 +
 src/libsysprof/preload/sysprof-memory-collector.c | 231 ++++++++++++++++++++++
 src/tests/test-capture.c                          |  62 ++++++
 src/tools/sysprof-dump.c                          |   4 +-
 5 files changed, 307 insertions(+), 2 deletions(-)
---
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index 2b40744..3796ce1 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -146,4 +146,6 @@ pkgconfig.generate(
 
 install_headers(libsysprof_public_headers, subdir: sysprof_header_subdir)
 
+subdir('preload')
+
 endif
diff --git a/src/libsysprof/preload/meson.build b/src/libsysprof/preload/meson.build
new file mode 100644
index 0000000..11d94fa
--- /dev/null
+++ b/src/libsysprof/preload/meson.build
@@ -0,0 +1,10 @@
+libsysprof_memory_preload_deps = [
+  libsysprof_capture_dep,
+  cc.find_library('dl', required: false),
+]
+
+libsysprof_memory_preload = shared_library('sysprof-memory-collector',
+  ['sysprof-memory-collector.c'],
+  dependencies: libsysprof_memory_preload_deps,
+)
+
diff --git a/src/libsysprof/preload/sysprof-memory-collector.c 
b/src/libsysprof/preload/sysprof-memory-collector.c
new file mode 100644
index 0000000..d40714a
--- /dev/null
+++ b/src/libsysprof/preload/sysprof-memory-collector.c
@@ -0,0 +1,231 @@
+#define _GNU_SOURCE
+
+#include <dlfcn.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sysprof-capture.h>
+#include <unistd.h>
+
+typedef void *(* RealMalloc)        (size_t);
+typedef void  (* RealFree)          (void *);
+typedef void *(* RealCalloc)        (size_t, size_t);
+typedef void *(* RealRealloc)       (void *, size_t);
+typedef void *(* RealAlignedAlloc)  (size_t, size_t);
+typedef int   (* RealPosixMemalign) (void **, size_t, size_t);
+typedef void *(* RealMemalign)      (size_t, size_t);
+
+typedef struct
+{
+  char buf[4092];
+  int  off;
+} ScratchAlloc;
+
+static void  hook_memtable   (void);
+static void *scratch_malloc  (size_t);
+static void *scratch_realloc (void *, size_t);
+static void *scratch_calloc  (size_t, size_t);
+static void  scratch_free    (void *);
+
+static G_LOCK_DEFINE (writer);
+static SysprofCaptureWriter *writer;
+static int hooked;
+static int pid;
+static ScratchAlloc scratch;
+static RealCalloc real_calloc = scratch_calloc;
+static RealFree real_free = scratch_free;
+static RealMalloc real_malloc = scratch_malloc;
+static RealRealloc real_realloc = scratch_realloc;
+static RealAlignedAlloc real_aligned_alloc;
+static RealPosixMemalign real_posix_memalign;
+static RealMemalign real_memalign;
+
+static void *
+scratch_malloc (size_t size)
+{
+  hook_memtable ();
+  return real_malloc (size);
+}
+
+static void *
+scratch_realloc (void   *ptr,
+                 size_t  size)
+{
+  hook_memtable ();
+  return real_realloc (ptr, size);
+}
+
+static void *
+scratch_calloc (size_t nmemb,
+                size_t size)
+{
+  void *ret;
+
+  /* re-entrant, but forces early hook in case calloc is
+   * called before any of our other hooks.
+   */
+  if (!hooked)
+    hook_memtable ();
+ 
+  size *= nmemb;
+  ret = &scratch.buf[scratch.off];
+  scratch.off += size;
+
+  return ret;
+}
+
+static void
+scratch_free (void *ptr)
+{
+  if ((char *)ptr >= scratch.buf && (char *)ptr < scratch.buf + scratch.off)
+    return;
+}
+
+static void
+flush_writer (void)
+{
+  G_LOCK (writer);
+  sysprof_capture_writer_flush (writer);
+  G_UNLOCK (writer);
+}
+
+static void
+hook_memtable (void)
+{
+  const gchar *env;
+
+  if (hooked)
+    return;
+
+  hooked = 1;
+
+  real_calloc = dlsym (RTLD_NEXT, "calloc");
+  real_free = dlsym (RTLD_NEXT, "free");
+  real_malloc = dlsym (RTLD_NEXT, "malloc");
+  real_realloc = dlsym (RTLD_NEXT, "realloc");
+  real_aligned_alloc = dlsym (RTLD_NEXT, "aligned_alloc");
+  real_posix_memalign = dlsym (RTLD_NEXT, "posix_memalign");
+  real_memalign = dlsym (RTLD_NEXT, "memalign");
+
+  unsetenv ("LD_PRELOAD");
+
+  pid = getpid ();
+
+  /* TODO: We want an API that let's us create a new writer
+   * per-thread instead of something like this (or using an
+   * environment variable). That will require a control channel
+   * to sysprof to request new writer/muxed APIs.
+   */
+
+  env = getenv ("MEMORY_TRACE_FD");
+
+  if (env != NULL)
+    {
+      int fd = atoi (env);
+
+      if (fd > 0)
+        writer = sysprof_capture_writer_new_from_fd (fd, 0);
+    }
+
+  if (writer == NULL)
+    writer = sysprof_capture_writer_new ("memory.syscap", 0);
+
+  atexit (flush_writer);
+}
+
+#define gettid() syscall(__NR_gettid, 0)
+
+static inline void
+track_malloc (void   *ptr,
+              size_t  size)
+{
+  if G_UNLIKELY (!writer)
+    return;
+
+  G_LOCK (writer);
+
+  /* TODO: To make this really useful, we need to take a backtrace
+   * of the current stack so that we can show the user allocations
+   * within the application. However, for now we just want to get
+   * the allocation information to draw fragmentation.
+   */
+  sysprof_capture_writer_add_memory_alloc (writer,
+                                           SYSPROF_CAPTURE_CURRENT_TIME,
+                                           sched_getcpu (),
+                                           pid,
+                                           gettid(),
+                                           GPOINTER_TO_SIZE (ptr),
+                                           size,
+                                           NULL, /* TODO: Sample */
+                                           0);
+  G_UNLOCK (writer);
+}
+
+static inline void
+track_free (void *ptr)
+{
+  if G_UNLIKELY (!writer)
+    return;
+
+  G_LOCK (writer);
+  sysprof_capture_writer_add_memory_free (writer,
+                                          SYSPROF_CAPTURE_CURRENT_TIME,
+                                          sched_getcpu (),
+                                          pid,
+                                          gettid(),
+                                          GPOINTER_TO_SIZE (ptr));
+  G_UNLOCK (writer);
+}
+
+void *
+malloc (size_t size)
+{
+  void *ret = real_malloc (size);
+  track_malloc (ret, size);
+  return ret;
+}
+
+void *
+calloc (size_t nmemb,
+        size_t size)
+{
+  void *ret = real_calloc (nmemb, size);
+  track_malloc (ret, size);
+  return ret;
+}
+
+void
+free (void *ptr)
+{
+  real_free (ptr);
+  track_free (ptr);
+}
+
+void *
+aligned_alloc (size_t alignment,
+               size_t size)
+{
+  void *ret = real_aligned_alloc (alignment, size);
+  track_malloc (ret, size);
+  return ret;
+}
+
+int
+posix_memalign (void   **memptr,
+                size_t   alignment,
+                size_t   size)
+{
+  int ret = real_posix_memalign (memptr, alignment, size);
+  track_malloc (*memptr, size);
+  return ret;
+}
+
+void *
+memalign (size_t alignment,
+          size_t size)
+{
+  void *ret = real_memalign (alignment, size);
+  track_malloc (ret, size);
+  return ret;
+}
diff --git a/src/tests/test-capture.c b/src/tests/test-capture.c
index 5f8eb3d..3102788 100644
--- a/src/tests/test-capture.c
+++ b/src/tests/test-capture.c
@@ -900,6 +900,67 @@ test_reader_writer_cat_jitmap (void)
   g_unlink ("jitmap-joined.syscap");
 }
 
+static void
+test_writer_memory_alloc_free (void)
+{
+  SysprofCaptureWriter *writer;
+  SysprofCaptureReader *reader;
+  GError *error = NULL;
+  SysprofCaptureAddress addrs[20] = {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+    11, 12, 13, 14, 15, 16, 17, 18, 19,
+  };
+  gboolean r;
+
+  writer = sysprof_capture_writer_new ("memory.syscap", 0);
+
+  for (guint i = 0; i < 20; i++)
+    {
+      r = sysprof_capture_writer_add_memory_alloc (writer,
+                                                   SYSPROF_CAPTURE_CURRENT_TIME,
+                                                   i % 4,
+                                                   i % 3,
+                                                   i % 7,
+                                                   i,
+                                                   i * 2,
+                                                   addrs,
+                                                   i);
+      g_assert_true (r);
+    }
+
+  sysprof_capture_writer_flush (writer);
+
+  reader = sysprof_capture_writer_create_reader (writer, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (reader);
+
+  for (guint i = 0; i < 20; i++)
+    {
+      const SysprofCaptureMemoryAlloc *ev;
+
+      ev = sysprof_capture_reader_read_memory_alloc (reader);
+      g_assert_nonnull (ev);
+      g_assert_cmpint (ev->frame.type, ==, SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC);
+
+      g_assert_cmpint (ev->frame.cpu, ==, i % 4);
+      g_assert_cmpint (ev->frame.pid, ==, i % 3);
+      g_assert_cmpint (ev->tid, ==, i % 7);
+      g_assert_cmpint (ev->alloc_addr, ==, i);
+      g_assert_cmpint (ev->alloc_size, ==, i * 2);
+      g_assert_cmpint (ev->n_addrs, ==, i);
+
+      for (guint j = 0; j < i; j++)
+        {
+          g_assert_cmpint (ev->addrs[j], ==, j);
+        }
+    }
+
+  sysprof_capture_writer_unref (writer);
+  sysprof_capture_reader_unref (reader);
+
+  g_unlink ("memory.syscap");
+}
+
 int
 main (int argc,
       char *argv[])
@@ -907,6 +968,7 @@ main (int argc,
   sysprof_clock_init ();
   g_test_init (&argc, &argv, NULL);
   g_test_add_func ("/SysprofCapture/ReaderWriter", test_reader_basic);
+  g_test_add_func ("/SysprofCapture/ReaderWriter/alloc_free", test_writer_memory_alloc_free);
   g_test_add_func ("/SysprofCapture/Writer/splice", test_writer_splice);
   g_test_add_func ("/SysprofCapture/Reader/splice", test_reader_splice);
   g_test_add_func ("/SysprofCapture/ReaderWriter/log", test_reader_writer_log);
diff --git a/src/tools/sysprof-dump.c b/src/tools/sysprof-dump.c
index a35cb7e..fe3da77 100644
--- a/src/tools/sysprof-dump.c
+++ b/src/tools/sysprof-dump.c
@@ -304,7 +304,7 @@ main (gint argc,
             const SysprofCaptureMemoryAlloc *ev = sysprof_capture_reader_read_memory_alloc (reader);
             gdouble ptime = (ev->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
 
-            g_print ("MALLOC: pid=%d tid=%d addr=%"G_GUINT64_FORMAT"x size=%"G_GUINT64_FORMAT" 
time=%"G_GINT64_FORMAT" (%lf)\n",
+            g_print ("MALLOC: pid=%d tid=%d addr=0x%"G_GINT64_MODIFIER"x size=%"G_GUINT64_FORMAT" 
time=%"G_GINT64_FORMAT" (%lf)\n",
                      ev->frame.pid, ev->tid,
                      ev->alloc_addr, ev->alloc_size,
                      ev->frame.time, ptime);
@@ -316,7 +316,7 @@ main (gint argc,
             const SysprofCaptureMemoryFree *ev = sysprof_capture_reader_read_memory_free (reader);
             gdouble ptime = (ev->frame.time - begin_time) / (gdouble)SYSPROF_NSEC_PER_SEC;
 
-            g_print ("FREE: pid=%d tid=%d addr=%"G_GUINT64_FORMAT"x time=%"G_GINT64_FORMAT" (%lf)\n",
+            g_print ("FREE: pid=%d tid=%d addr=0x%"G_GINT64_MODIFIER"x time=%"G_GINT64_FORMAT" (%lf)\n",
                      ev->frame.pid, ev->tid, ev->alloc_addr,
                      ev->frame.time, ptime);
           }


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