[sysprof/wip/visualizers] capture: add SpCaptureCursor and associated types



commit c19d0635aa7f147007cdefc6f2fb9dde326fbf8b
Author: Christian Hergert <chergert redhat com>
Date:   Sun Sep 25 00:25:08 2016 -0700

    capture: add SpCaptureCursor and associated types
    
    This API helps us simplify some of the tooling to iterate
    through a capture. In particular, we might want to setup a
    bunch of matches and then just iterate through the items.
    
    This can also allow delaying the iteration until the future
    which might be handy for visualizers which won't want to block
    the main loop.
    
    I'm not jazzed about the 64k buffer created for every cursor
    due to the SpCaptureReader copy, but it's probably not a big
    deal in practice until we start doing more exotic things.

 lib/Makefile.am             |    4 +
 lib/sp-capture-condition.c  |  193 ++++++++++++++++++++++++++++++++++++++++
 lib/sp-capture-condition.h  |   40 +++++++++
 lib/sp-capture-cursor.c     |  204 +++++++++++++++++++++++++++++++++++++++++++
 lib/sp-capture-cursor.h     |   55 ++++++++++++
 lib/sp-capture-types.h      |    6 +-
 lib/sysprof.h               |    2 +
 tests/test-capture-cursor.c |   58 ++++++++++++
 8 files changed, 560 insertions(+), 2 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d6f6a90..b31ff98 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -45,6 +45,8 @@ headers_DATA = \
        sysprof-version.h \
        sp-address.h \
        sp-callgraph-profile.h \
+       sp-capture-condition.h \
+       sp-capture-cursor.h \
        sp-capture-reader.h \
        sp-capture-writer.h \
        sp-capture-types.h \
@@ -71,6 +73,8 @@ libsysprof_@API_VERSION@_la_SOURCES = \
        sp-address.c \
        sp-callgraph-profile.c \
        sp-callgraph-profile-private.h \
+       sp-capture-condition.c \
+       sp-capture-cursor.c \
        sp-capture-reader.c \
        sp-capture-writer.c \
        sp-clock.c \
diff --git a/lib/sp-capture-condition.c b/lib/sp-capture-condition.c
new file mode 100644
index 0000000..d7ff7a0
--- /dev/null
+++ b/lib/sp-capture-condition.c
@@ -0,0 +1,193 @@
+/* sp-capture-condition.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#define G_LOG_DOMAIN "sp-capture-condition"
+
+#include <string.h>
+
+#include "sp-capture-condition.h"
+
+typedef enum
+{
+  SP_CAPTURE_CONDITION_WHERE_TYPE_IN,
+  SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN,
+  SP_CAPTURE_CONDITION_WHERE_PID_IN,
+} SpCaptureConditionType;
+
+struct _SpCaptureCondition
+{
+  SpCaptureConditionType type;
+  union {
+    GArray *where_type_in;
+    struct {
+      gint64 begin;
+      gint64 end;
+    } where_time_between;
+    GArray *where_pid_in;
+  } u;
+};
+
+gboolean
+sp_capture_condition_match (const SpCaptureCondition *self,
+                            const SpCaptureFrame     *frame)
+{
+  g_assert (self != NULL);
+  g_assert (frame != NULL);
+
+  switch (self->type)
+    {
+    case SP_CAPTURE_CONDITION_WHERE_TYPE_IN:
+      for (guint i = 0; i < self->u.where_type_in->len; i++)
+        {
+          if (frame->type == g_array_index (self->u.where_type_in, SpCaptureFrameType, i))
+            return TRUE;
+        }
+      return FALSE;
+
+    case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN:
+      return (frame->time >= self->u.where_time_between.begin && frame->time <= 
self->u.where_time_between.end);
+
+    case SP_CAPTURE_CONDITION_WHERE_PID_IN:
+      for (guint i = 0; i < self->u.where_pid_in->len; i++)
+        {
+          if (frame->pid == g_array_index (self->u.where_pid_in, GPid, i))
+            return TRUE;
+        }
+      return FALSE;
+
+    default:
+      break;
+    }
+
+  g_assert_not_reached ();
+
+  return FALSE;
+}
+
+static SpCaptureCondition *
+sp_capture_condition_copy (const SpCaptureCondition *self)
+{
+  SpCaptureCondition *copy;
+
+  copy = g_slice_new0 (SpCaptureCondition);
+  copy->type = self->type;
+
+  switch (self->type)
+    {
+    case SP_CAPTURE_CONDITION_WHERE_TYPE_IN:
+      return sp_capture_condition_new_where_type_in (
+          self->u.where_type_in->len, (const SpCaptureFrameType *)self->u.where_type_in->data);
+
+    case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN:
+      break;
+
+    case SP_CAPTURE_CONDITION_WHERE_PID_IN:
+      return sp_capture_condition_new_where_pid_in (
+          self->u.where_pid_in->len, (const GPid *)self->u.where_pid_in->data);
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return copy;
+}
+
+static void
+sp_capture_condition_free (SpCaptureCondition *self)
+{
+  switch (self->type)
+    {
+    case SP_CAPTURE_CONDITION_WHERE_TYPE_IN:
+      g_array_free (self->u.where_type_in, TRUE);
+      break;
+
+    case SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN:
+      break;
+
+    case SP_CAPTURE_CONDITION_WHERE_PID_IN:
+      g_array_free (self->u.where_pid_in, TRUE);
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  g_slice_free (SpCaptureCondition, self);
+}
+
+G_DEFINE_BOXED_TYPE (SpCaptureCondition,
+                     sp_capture_condition,
+                     sp_capture_condition_copy,
+                     sp_capture_condition_free)
+
+SpCaptureCondition *
+sp_capture_condition_new_where_type_in (guint                     n_types,
+                                        const SpCaptureFrameType *types)
+{
+  SpCaptureCondition *self;
+
+  g_return_val_if_fail (types != NULL, NULL);
+
+  self = g_slice_new0 (SpCaptureCondition);
+  self->type = SP_CAPTURE_CONDITION_WHERE_TYPE_IN;
+  self->u.where_type_in = g_array_sized_new (FALSE, FALSE, sizeof (SpCaptureFrameType), n_types);
+  g_array_set_size (self->u.where_type_in, n_types);
+  memcpy (self->u.where_type_in->data, types, sizeof (SpCaptureFrameType) * n_types);
+
+  return self;
+}
+
+SpCaptureCondition *
+sp_capture_condition_new_where_time_between (gint64 begin_time,
+                                             gint64 end_time)
+{
+  SpCaptureCondition *self;
+
+  if G_UNLIKELY (begin_time > end_time)
+    {
+      gint64 tmp = begin_time;
+      begin_time = end_time;
+      end_time = tmp;
+    }
+
+  self = g_slice_new0 (SpCaptureCondition);
+  self->type = SP_CAPTURE_CONDITION_WHERE_TIME_BETWEEN;
+  self->u.where_time_between.begin = begin_time;
+  self->u.where_time_between.end = end_time;
+
+  return self;
+}
+
+SpCaptureCondition *
+sp_capture_condition_new_where_pid_in (guint       n_pids,
+                                       const GPid *pids)
+{
+  SpCaptureCondition *self;
+
+  g_return_val_if_fail (pids != NULL, NULL);
+
+  self = g_slice_new0 (SpCaptureCondition);
+  self->type = SP_CAPTURE_CONDITION_WHERE_PID_IN;
+  self->u.where_pid_in = g_array_sized_new (FALSE, FALSE, sizeof (GPid), n_pids);
+  g_array_set_size (self->u.where_pid_in, n_pids);
+  memcpy (self->u.where_pid_in->data, pids, sizeof (GPid) * n_pids);
+
+  return self;
+}
diff --git a/lib/sp-capture-condition.h b/lib/sp-capture-condition.h
new file mode 100644
index 0000000..67f7012
--- /dev/null
+++ b/lib/sp-capture-condition.h
@@ -0,0 +1,40 @@
+/* sp-capture-condition.h
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef SP_CAPTURE_CONDITION_H
+#define SP_CAPTURE_CONDITION_H
+
+#include "sp-capture-types.h"
+
+G_BEGIN_DECLS
+
+#define SP_TYPE_CAPTURE_CONDITION (sp_capture_condition_get_type())
+
+GType               sp_capture_condition_get_type               (void);
+SpCaptureCondition *sp_capture_condition_new_where_type_in      (guint                     n_types,
+                                                                 const SpCaptureFrameType *types);
+SpCaptureCondition *sp_capture_condition_new_where_time_between (gint64                    begin_time,
+                                                                 gint64                    end_time);
+SpCaptureCondition *sp_capture_condition_new_where_pid_in       (guint                     n_pids,
+                                                                 const GPid               *pids);
+gboolean            sp_capture_condition_match                  (const SpCaptureCondition *self,
+                                                                 const SpCaptureFrame     *frame);
+
+G_END_DECLS
+
+#endif /* SP_CAPTURE_CONDITION_H */
diff --git a/lib/sp-capture-cursor.c b/lib/sp-capture-cursor.c
new file mode 100644
index 0000000..de25a9f
--- /dev/null
+++ b/lib/sp-capture-cursor.c
@@ -0,0 +1,204 @@
+/* sp-capture-cursor.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#define G_LOG_DOMAIN "sp-capture-cursor"
+
+#include "sp-capture-condition.h"
+#include "sp-capture-cursor.h"
+#include "sp-capture-reader.h"
+
+#define READ_DELEGATE(f) ((ReadDelegate)(f))
+
+typedef const SpCaptureFrame *(*ReadDelegate) (SpCaptureReader *);
+
+struct _SpCaptureCursor
+{
+  GObject          parent_instance;
+  GPtrArray       *conditions;
+  SpCaptureReader *reader;
+  guint            reversed : 1;
+};
+
+G_DEFINE_TYPE (SpCaptureCursor, sp_capture_cursor, G_TYPE_OBJECT)
+
+static void
+destroy_condition (gpointer data)
+{
+  g_boxed_free (SP_TYPE_CAPTURE_CONDITION, data);
+}
+
+static void
+sp_capture_cursor_finalize (GObject *object)
+{
+  SpCaptureCursor *self = (SpCaptureCursor *)object;
+
+  g_clear_pointer (&self->conditions, g_ptr_array_unref);
+  g_clear_pointer (&self->reader, sp_capture_reader_unref);
+
+  G_OBJECT_CLASS (sp_capture_cursor_parent_class)->finalize (object);
+}
+
+static void
+sp_capture_cursor_class_init (SpCaptureCursorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = sp_capture_cursor_finalize;
+}
+
+static void
+sp_capture_cursor_init (SpCaptureCursor *self)
+{
+  self->conditions = g_ptr_array_new_with_free_func (destroy_condition);
+}
+
+void
+sp_capture_cursor_foreach (SpCaptureCursor         *self,
+                           SpCaptureCursorCallback  callback,
+                           gpointer                 user_data)
+{
+  g_return_if_fail (SP_IS_CAPTURE_CURSOR (self));
+  g_return_if_fail (self->reader != NULL);
+  g_return_if_fail (callback != NULL);
+
+  for (;;)
+    {
+      const SpCaptureFrame *frame;
+      SpCaptureFrameType type = 0;
+      ReadDelegate delegate;
+
+      if (!sp_capture_reader_peek_type (self->reader, &type))
+        return;
+
+      switch (type)
+        {
+        case SP_CAPTURE_FRAME_TIMESTAMP:
+          delegate = READ_DELEGATE (sp_capture_reader_read_timestamp);
+          break;
+
+        case SP_CAPTURE_FRAME_SAMPLE:
+          delegate = READ_DELEGATE (sp_capture_reader_read_sample);
+          break;
+
+        case SP_CAPTURE_FRAME_MAP:
+          delegate = READ_DELEGATE (sp_capture_reader_read_map);
+          break;
+
+        case SP_CAPTURE_FRAME_PROCESS:
+          delegate = READ_DELEGATE (sp_capture_reader_read_process);
+          break;
+
+        case SP_CAPTURE_FRAME_FORK:
+          delegate = READ_DELEGATE (sp_capture_reader_read_fork);
+          break;
+
+        case SP_CAPTURE_FRAME_EXIT:
+          delegate = READ_DELEGATE (sp_capture_reader_read_exit);
+          break;
+
+        case SP_CAPTURE_FRAME_JITMAP:
+          delegate = READ_DELEGATE (sp_capture_reader_read_jitmap);
+          break;
+
+        case SP_CAPTURE_FRAME_CTRDEF:
+          delegate = READ_DELEGATE (sp_capture_reader_read_counter_define);
+          break;
+
+        case SP_CAPTURE_FRAME_CTRSET:
+          delegate = READ_DELEGATE (sp_capture_reader_read_counter_set);
+          break;
+
+        default:
+          if (!sp_capture_reader_skip (self->reader))
+            return;
+          delegate = NULL;
+          break;
+        }
+
+      if (delegate != NULL && NULL == (frame = delegate (self->reader)))
+        return;
+
+      if (self->conditions->len == 0)
+        {
+          if (!callback (frame, user_data))
+            return;
+        }
+      else
+        {
+          for (guint i = 0; i < self->conditions->len; i++)
+            {
+              SpCaptureCondition *condition = g_ptr_array_index (self->conditions, i);
+
+              if (sp_capture_condition_match (condition, frame))
+                {
+                  if (!callback (frame, user_data))
+                    return;
+                  break;
+                }
+            }
+        }
+    }
+}
+
+void
+sp_capture_cursor_reset (SpCaptureCursor *self)
+{
+  g_return_if_fail (SP_IS_CAPTURE_CURSOR (self));
+  g_return_if_fail (self->reader != NULL);
+
+  sp_capture_reader_reset (self->reader);
+}
+
+void
+sp_capture_cursor_reverse (SpCaptureCursor *self)
+{
+  g_return_if_fail (SP_IS_CAPTURE_CURSOR (self));
+
+  self->reversed = !self->reversed;
+}
+
+/**
+ * sp_capture_cursor_add_condition:
+ * @self: An #SpCaptureCursor
+ * @condition: (transfer full): An #SpCaptureCondition
+ *
+ * Adds the condition to the cursor. This condition must evaluate to
+ * true or a given #SpCapureFrame will not be matched.
+ */
+void
+sp_capture_cursor_add_condition (SpCaptureCursor    *self,
+                                 SpCaptureCondition *condition)
+{
+  g_return_if_fail (SP_IS_CAPTURE_CURSOR (self));
+  g_return_if_fail (condition != NULL);
+
+  g_ptr_array_add (self->conditions, condition);
+}
+
+SpCaptureCursor *
+sp_capture_cursor_new (SpCaptureReader *reader)
+{
+  SpCaptureCursor *self;
+
+  g_return_val_if_fail (reader != NULL, NULL);
+
+  self = g_object_new (SP_TYPE_CAPTURE_CURSOR, NULL);
+  self->reader = sp_capture_reader_copy (reader);
+
+  return self;
+}
diff --git a/lib/sp-capture-cursor.h b/lib/sp-capture-cursor.h
new file mode 100644
index 0000000..e59fab2
--- /dev/null
+++ b/lib/sp-capture-cursor.h
@@ -0,0 +1,55 @@
+/* sp-capture-cursor.h
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef SP_CAPTURE_CURSOR_H
+#define SP_CAPTURE_CURSOR_H
+
+#include "sp-capture-types.h"
+
+G_BEGIN_DECLS
+
+#define SP_TYPE_CAPTURE_CURSOR (sp_capture_cursor_get_type())
+
+G_DECLARE_FINAL_TYPE (SpCaptureCursor, sp_capture_cursor, SP, CAPTURE_CURSOR, GObject)
+
+/**
+ * SpCaptureCursorCallback:
+ *
+ * This is the prototype for callbacks that are called for each frame
+ * matching the cursor query.
+ *
+ * Functions matching this typedef should return %TRUE if they want the
+ * the caller to stop iteration of cursor.
+ *
+ * Returns: %TRUE if iteration should stop, otherwise %FALSE.
+ */
+typedef gboolean (*SpCaptureCursorCallback) (const SpCaptureFrame *frame,
+                                             gpointer              user_data);
+
+SpCaptureCursor *sp_capture_cursor_new            (SpCaptureReader         *reader);
+void             sp_capture_cursor_foreach        (SpCaptureCursor         *self,
+                                                   SpCaptureCursorCallback  callback,
+                                                   gpointer                 user_data);
+void             sp_capture_cursor_reset          (SpCaptureCursor         *self);
+void             sp_capture_cursor_reverse        (SpCaptureCursor         *self);
+void             sp_capture_cursor_add_condition  (SpCaptureCursor         *self,
+                                                   SpCaptureCondition      *condition);
+
+G_END_DECLS
+
+#endif /* SP_CAPTURE_CURSOR_H */
diff --git a/lib/sp-capture-types.h b/lib/sp-capture-types.h
index c0dd2b2..65d6b69 100644
--- a/lib/sp-capture-types.h
+++ b/lib/sp-capture-types.h
@@ -42,8 +42,10 @@ G_BEGIN_DECLS
 #define SP_CAPTURE_COUNTER_INT64  0
 #define SP_CAPTURE_COUNTER_DOUBLE 1
 
-typedef struct _SpCaptureReader SpCaptureReader;
-typedef struct _SpCaptureWriter SpCaptureWriter;
+typedef struct _SpCaptureReader    SpCaptureReader;
+typedef struct _SpCaptureWriter    SpCaptureWriter;
+typedef struct _SpCaptureCursor    SpCaptureCursor;
+typedef struct _SpCaptureCondition SpCaptureCondition;
 
 typedef guint64 SpCaptureAddress;
 
diff --git a/lib/sysprof.h b/lib/sysprof.h
index 30e17c9..5f3b696 100644
--- a/lib/sysprof.h
+++ b/lib/sysprof.h
@@ -26,6 +26,8 @@ G_BEGIN_DECLS
 #define SYSPROF_INSIDE
 # include "sp-address.h"
 # include "sp-callgraph-profile.h"
+# include "sp-capture-condition.h"
+# include "sp-capture-cursor.h"
 # include "sp-capture-reader.h"
 # include "sp-capture-writer.h"
 # include "sp-clock.h"
diff --git a/tests/test-capture-cursor.c b/tests/test-capture-cursor.c
new file mode 100644
index 0000000..14e0e76
--- /dev/null
+++ b/tests/test-capture-cursor.c
@@ -0,0 +1,58 @@
+#include <glib/gstdio.h>
+#include <sysprof.h>
+
+static gboolean
+increment (const SpCaptureFrame *frame,
+           gpointer              user_data)
+{
+  gint *count= user_data;
+  (*count)++;
+  return TRUE;
+}
+
+static void
+test_cursor_basic (void)
+{
+  SpCaptureReader *reader;
+  SpCaptureWriter *writer;
+  SpCaptureCursor *cursor;
+  GError *error = NULL;
+  gint64 t = SP_CAPTURE_CURRENT_TIME;
+  guint i;
+  gint r;
+  gint count = 0;
+
+  writer = sp_capture_writer_new ("capture-file", 0);
+  g_assert (writer != NULL);
+
+  reader = sp_capture_reader_new ("capture-file", &error);
+  g_assert_no_error (error);
+  g_assert (reader != NULL);
+
+  for (i = 0; i < 100; i++)
+    {
+      r = sp_capture_writer_add_timestamp (writer, t, i, -1);
+      g_assert_cmpint (r, ==, TRUE);
+    }
+
+  sp_capture_writer_flush (writer);
+
+  cursor = sp_capture_cursor_new (reader);
+  sp_capture_cursor_foreach (cursor, increment, &count);
+  g_assert_cmpint (count, ==, 100);
+  g_clear_object (&cursor);
+
+  sp_capture_reader_unref (reader);
+  sp_capture_writer_unref (writer);
+
+  g_unlink ("capture-file");
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/SpCaptureCursor/basic", test_cursor_basic);
+  return g_test_run ();
+}


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