[sysprof/wip/chergert/mem-preload] memory: start on a memory profile



commit c6c5d77c314bdc8c9118d96162fae6bdb945bafe
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jan 31 15:26:40 2020 -0800

    memory: start on a memory profile
    
    The long term goal here is to get the stack from allocations as well as
    get information about what is still on the heap. This can be useful when
    tracking down fragmentation issues for long running modules or temporary
    allocations which could be better served from secondary allocators.

 src/libsysprof/meson.build              |  22 +--
 src/libsysprof/sysprof-memory-profile.c | 236 ++++++++++++++++++++++++++++++++
 src/libsysprof/sysprof-memory-profile.h |  43 ++++++
 src/libsysprof/sysprof.h                |   1 +
 src/tests/meson.build                   |   6 +
 src/tests/show-page-usage.c             | 196 ++++++++++++++++++++++++++
 6 files changed, 496 insertions(+), 8 deletions(-)
---
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index bcb57db..459b093 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -16,6 +16,7 @@ libsysprof_public_sources = [
   'sysprof-kernel-symbol.c',
   'sysprof-kernel-symbol-resolver.c',
   'sysprof-local-profiler.c',
+  'sysprof-memory-profile.c',
   'sysprof-netdev-source.c',
   'sysprof-process-model.c',
   'sysprof-process-model-item.c',
@@ -45,6 +46,7 @@ libsysprof_public_headers = [
   'sysprof-kernel-symbol.h',
   'sysprof-kernel-symbol-resolver.h',
   'sysprof-local-profiler.h',
+  'sysprof-memory-profile.h',
   'sysprof-process-model.h',
   'sysprof-process-model-item.h',
   'sysprof-profile.h',
@@ -79,10 +81,22 @@ libsysprof_private_sources = [
 
 libsysprof_public_sources += libsysprof_capture_sources
 
+librax = static_library('rax', ['rax.c'],
+               c_args: [ '-Wno-declaration-after-statement',
+                         '-Wno-format-nonliteral',
+                         '-Wno-shadow' ],
+)
+
+librax_dep = declare_dependency(
+           link_whole: librax,
+  include_directories: include_directories('.'),
+)
+
 libsysprof_deps = [
   gio_dep,
   gio_unix_dep,
   polkit_dep,
+  librax_dep,
 ]
 
 if host_machine.system() == 'linux'
@@ -110,13 +124,6 @@ if host_machine.system() != 'darwin'
   libsysprof_deps += [cxx.find_library('stdc++')]
 endif
 
-librax = static_library('rax', ['rax.c'],
-   c_args: [ '-Wno-declaration-after-statement',
-             '-Wno-format-nonliteral',
-             '-Wno-shadow' ],
-  install: false
-)
-
 libsysprof = shared_library(
   'sysprof-@0@'.format(libsysprof_api_version),
   libsysprof_public_sources + libsysprof_private_sources,
@@ -129,7 +136,6 @@ libsysprof = shared_library(
                 install: true,
             install_dir: get_option('libdir'),
   gnu_symbol_visibility: 'hidden',
-             link_whole: [ librax ],
 )
 
 libsysprof_dep = declare_dependency(
diff --git a/src/libsysprof/sysprof-memory-profile.c b/src/libsysprof/sysprof-memory-profile.c
new file mode 100644
index 0000000..5caf3df
--- /dev/null
+++ b/src/libsysprof/sysprof-memory-profile.c
@@ -0,0 +1,236 @@
+/* sysprof-memory-profile.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "sysprof-memory-profile"
+
+#include "config.h"
+
+#include <sysprof-capture.h>
+
+#include "sysprof-memory-profile.h"
+
+#include "rax.h"
+
+struct _SysprofMemoryProfile
+{
+  GObject               parent_instance;
+  SysprofCaptureReader *reader;
+  rax *rax;
+};
+
+typedef struct
+{
+  SysprofCaptureReader *reader;
+  rax *rax;
+} Generate;
+
+static void profile_iface_init (SysprofProfileInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (SysprofMemoryProfile, sysprof_memory_profile, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_PROFILE, profile_iface_init))
+
+static void
+generate_free (Generate *g)
+{
+  g_clear_pointer (&g->reader, sysprof_capture_reader_unref);
+  g_clear_pointer (&g->rax, raxFree);
+  g_slice_free (Generate, g);
+}
+
+static void
+sysprof_memory_profile_finalize (GObject *object)
+{
+  SysprofMemoryProfile *self = (SysprofMemoryProfile *)object;
+
+  g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
+  g_clear_pointer (&self->rax, raxFree);
+
+  G_OBJECT_CLASS (sysprof_memory_profile_parent_class)->finalize (object);
+}
+
+static void
+sysprof_memory_profile_class_init (SysprofMemoryProfileClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = sysprof_memory_profile_finalize;
+}
+
+static void
+sysprof_memory_profile_init (SysprofMemoryProfile *self)
+{
+}
+
+SysprofProfile *
+sysprof_memory_profile_new (void)
+{
+  return g_object_new (SYSPROF_TYPE_MEMORY_PROFILE, NULL);
+}
+
+static void
+sysprof_memory_profile_set_reader (SysprofProfile       *profile,
+                                   SysprofCaptureReader *reader)
+{
+  SysprofMemoryProfile *self = (SysprofMemoryProfile *)profile;
+
+  g_assert (SYSPROF_IS_MEMORY_PROFILE (self));
+  g_assert (reader != NULL);
+
+  if (reader != self->reader)
+    {
+      g_clear_pointer (&self->reader, sysprof_capture_reader_unref);
+      self->reader = sysprof_capture_reader_ref (reader);
+    }
+}
+
+static SysprofCaptureCursor *
+create_cursor (SysprofCaptureReader *reader)
+{
+  static SysprofCaptureFrameType types[] = {
+    SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC,
+    SYSPROF_CAPTURE_FRAME_MEMORY_FREE,
+  };
+  SysprofCaptureCursor *cursor;
+  SysprofCaptureCondition *cond;
+
+  cond = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types);
+  cursor = sysprof_capture_cursor_new (reader);
+  sysprof_capture_cursor_add_condition (cursor, cond);
+
+  return cursor;
+}
+
+static gboolean
+cursor_foreach_cb (const SysprofCaptureFrame *frame,
+                   gpointer                   user_data)
+{
+  Generate *g = user_data;
+
+  g_assert (frame != NULL);
+  g_assert (frame->type == SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC ||
+            frame->type == SYSPROF_CAPTURE_FRAME_MEMORY_FREE);
+
+  if (frame->type == SYSPROF_CAPTURE_FRAME_MEMORY_ALLOC)
+    {
+      const SysprofCaptureMemoryAlloc *ev = (const SysprofCaptureMemoryAlloc *)frame;
+
+      raxInsert (g->rax,
+                 (guint8 *)&ev->alloc_addr,
+                 sizeof ev->alloc_addr,
+                 (gpointer)ev->alloc_size,
+                 NULL);
+    }
+  else if (frame->type == SYSPROF_CAPTURE_FRAME_MEMORY_FREE)
+    {
+      const SysprofCaptureMemoryFree *ev = (const SysprofCaptureMemoryFree *)frame;
+
+      raxRemove (g->rax,
+                 (guint8 *)&ev->alloc_addr,
+                 sizeof ev->alloc_addr,
+                 NULL);
+    }
+
+  return TRUE;
+}
+
+static void
+sysprof_memory_profile_generate_worker (GTask        *task,
+                                        gpointer      source_object,
+                                        gpointer      task_data,
+                                        GCancellable *cancellable)
+{
+  SysprofCaptureCursor *cursor;
+  Generate *g = task_data;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (g != NULL);
+  g_assert (g->reader != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  cursor = create_cursor (g->reader);
+  sysprof_capture_cursor_foreach (cursor, cursor_foreach_cb, g);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+sysprof_memory_profile_generate (SysprofProfile      *profile,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  SysprofMemoryProfile *self = (SysprofMemoryProfile *)profile;
+  g_autoptr(GTask) task = NULL;
+  Generate *g;
+
+  g_assert (SYSPROF_IS_MEMORY_PROFILE (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (self->rax == NULL);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, sysprof_memory_profile_generate);
+
+  if (self->reader == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_INITIALIZED,
+                               "No capture reader has been set");
+      return;
+    }
+
+  g = g_slice_new0 (Generate);
+  g->reader = sysprof_capture_reader_copy (self->reader);
+  g->rax = raxNew ();
+
+  g_task_set_task_data (task, g, (GDestroyNotify) generate_free);
+  g_task_run_in_thread (task, sysprof_memory_profile_generate_worker);
+}
+
+static gboolean
+sysprof_memory_profile_generate_finish (SysprofProfile  *profile,
+                                        GAsyncResult    *result,
+                                        GError         **error)
+{
+  SysprofMemoryProfile *self = (SysprofMemoryProfile *)profile;
+  Generate *g;
+
+  g_assert (SYSPROF_IS_MEMORY_PROFILE (self));
+  g_assert (G_IS_TASK (result));
+
+  if ((g = g_task_get_task_data (G_TASK (result))))
+    self->rax = g_steal_pointer (&g->rax);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+profile_iface_init (SysprofProfileInterface *iface)
+{
+  iface->set_reader = sysprof_memory_profile_set_reader;
+  iface->generate = sysprof_memory_profile_generate;
+  iface->generate_finish = sysprof_memory_profile_generate_finish;
+}
+
+gpointer
+sysprof_memory_profile_get_native (SysprofMemoryProfile *self)
+{
+  return self->rax;
+}
diff --git a/src/libsysprof/sysprof-memory-profile.h b/src/libsysprof/sysprof-memory-profile.h
new file mode 100644
index 0000000..0f9334f
--- /dev/null
+++ b/src/libsysprof/sysprof-memory-profile.h
@@ -0,0 +1,43 @@
+/* sysprof-memory-profile.h
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (SYSPROF_INSIDE) && !defined (SYSPROF_COMPILATION)
+# error "Only <sysprof.h> can be included directly."
+#endif
+
+#include "sysprof-version-macros.h"
+
+#include "sysprof-profile.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_MEMORY_PROFILE (sysprof_memory_profile_get_type())
+
+SYSPROF_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (SysprofMemoryProfile, sysprof_memory_profile, SYSPROF, MEMORY_PROFILE, GObject)
+
+SYSPROF_AVAILABLE_IN_3_36
+SysprofProfile *sysprof_memory_profile_new        (void);
+SYSPROF_AVAILABLE_IN_3_36
+gpointer        sysprof_memory_profile_get_native (SysprofMemoryProfile *self);
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h
index da4b524..0485ec0 100644
--- a/src/libsysprof/sysprof.h
+++ b/src/libsysprof/sysprof.h
@@ -36,6 +36,7 @@ G_BEGIN_DECLS
 # include "sysprof-kernel-symbol-resolver.h"
 # include "sysprof-kernel-symbol.h"
 # include "sysprof-local-profiler.h"
+# include "sysprof-memory-profile.h"
 # include "sysprof-netdev-source.h"
 # include "sysprof-process-model-item.h"
 # include "sysprof-process-model.h"
diff --git a/src/tests/meson.build b/src/tests/meson.build
index c506d19..d063e34 100644
--- a/src/tests/meson.build
+++ b/src/tests/meson.build
@@ -64,6 +64,12 @@ test_resolvers = executable('test-resolvers',
   dependencies: test_deps,
 )
 
+show_page_usage = executable('show-page-usage',
+  [ 'show-page-usage.c' ],
+        c_args: test_cflags,
+  dependencies: test_deps + [ librax_dep,
+                              dependency('cairo') ],
+)
 
 if get_option('enable_gtk')
 
diff --git a/src/tests/show-page-usage.c b/src/tests/show-page-usage.c
new file mode 100644
index 0000000..2b4d4b7
--- /dev/null
+++ b/src/tests/show-page-usage.c
@@ -0,0 +1,196 @@
+/* show-page-usage.c
+ *
+ * Copyright 2020 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "config.h"
+
+#include <cairo.h>
+#include <stddef.h>
+#include <rax.h>
+#include <sysprof.h>
+
+static GMainLoop *main_loop;
+
+static gint
+u64_compare (gconstpointer a,
+             gconstpointer b)
+{
+  const guint64 *aptr = a;
+  const guint64 *bptr = b;
+
+  if (*aptr < *bptr)
+    return -1;
+  else if (*aptr > *bptr)
+    return 1;
+  else
+    return 0;
+}
+
+static void
+generate_cb (GObject      *object,
+             GAsyncResult *result,
+             gpointer      user_data)
+{
+  SysprofProfile *profile = (SysprofProfile *)object;
+  g_autoptr(GError) error = NULL;
+  GHashTable *seen;
+  GHashTableIter iter;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  GArray *ar;
+  raxIterator it;
+  rax *r;
+  gpointer k,v;
+
+  g_assert (SYSPROF_IS_MEMORY_PROFILE (profile));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!sysprof_profile_generate_finish (profile, result, &error))
+    {
+      g_printerr ("%s\n", error->message);
+      exit (EXIT_FAILURE);
+    }
+
+  r = sysprof_memory_profile_get_native (SYSPROF_MEMORY_PROFILE (profile));
+  seen = g_hash_table_new (NULL, NULL);
+
+  raxStart (&it, r);
+  raxSeek (&it, "^", NULL, 0);
+  while (raxNext (&it))
+    {
+      guint64 page;
+      guint64 addr;
+
+      memcpy (&addr, it.key, sizeof addr);
+      page = addr / 4096;
+
+      if (g_hash_table_contains (seen, GSIZE_TO_POINTER (page)))
+        continue;
+
+      g_hash_table_insert (seen, GSIZE_TO_POINTER (page), NULL);
+    }
+  raxStop (&it);
+
+  ar = g_array_sized_new (FALSE, FALSE, sizeof (guint64), g_hash_table_size (seen));
+
+  g_hash_table_iter_init (&iter, seen);
+  while (g_hash_table_iter_next (&iter, &k, &v))
+    {
+      guint64 key = GPOINTER_TO_SIZE (k);
+
+      g_array_append_val (ar, key);
+    }
+
+  g_array_sort (ar, u64_compare);
+
+  for (guint i = 0; i < ar->len; i++)
+    {
+      guint64 key = g_array_index (ar, guint64, i);
+
+      g_hash_table_insert (seen, GSIZE_TO_POINTER (key), GSIZE_TO_POINTER (i));
+    }
+
+  g_printerr ("We have %u pages to graph\n", ar->len);
+
+  surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, ar->len, (4096/16));
+  cr = cairo_create (surface);
+
+  cairo_set_line_width (cr, 1.0);
+  cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
+
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_rectangle (cr, 0, 0, ar->len, (4096/16));
+  cairo_fill (cr);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+
+  cairo_scale (cr, 1.0, 1.0/16.0);
+  cairo_translate (cr, .5, .5);
+
+  raxStart (&it, r);
+  raxSeek (&it, "^", NULL, 0);
+  while (raxNext (&it))
+    {
+      guint64 page;
+      guint64 addr;
+      guint64 size;
+      guint x;
+      guint y;
+
+      memcpy (&addr, it.key, sizeof addr);
+      page = addr / 4096;
+      size = GPOINTER_TO_SIZE (it.data);
+
+      x = GPOINTER_TO_UINT (g_hash_table_lookup (seen, GSIZE_TO_POINTER (page)));
+      y = addr % 4096;
+
+      /* TODO: Need size */
+
+      cairo_move_to (cr, x, y);
+      cairo_line_to (cr, x, y+size);
+    }
+  raxStop (&it);
+
+  cairo_stroke (cr);
+
+  cairo_surface_write_to_png (surface, "memory.png");
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  g_array_unref (ar);
+  g_hash_table_unref (seen);
+
+  g_main_loop_quit (main_loop);
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  SysprofCaptureReader *reader;
+  const gchar *filename = argv[1];
+  g_autoptr(SysprofProfile) memprof = NULL;
+  g_autoptr(GError) error = NULL;
+
+  if (argc < 2)
+    {
+      g_printerr ("usage: %s FILENAME\n", argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  if (!(reader = sysprof_capture_reader_new (filename, &error)))
+    {
+      g_printerr ("%s\n", error->message);
+      return EXIT_FAILURE;
+    }
+
+  memprof = sysprof_memory_profile_new ();
+  sysprof_profile_set_reader (memprof, reader);
+  sysprof_profile_generate (memprof, NULL, generate_cb, NULL);
+
+  g_main_loop_run (main_loop);
+  g_main_loop_unref (main_loop);
+
+  sysprof_capture_reader_unref (reader);
+
+  return EXIT_SUCCESS;
+}


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