[sysprof] profiler: extract SpProfiler into an interface
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof] profiler: extract SpProfiler into an interface
- Date: Thu, 14 Apr 2016 09:37:37 +0000 (UTC)
commit 88d3ae3b742cc0952dbae94985205fb24df124cd
Author: Christian Hergert <chergert redhat com>
Date: Thu Apr 14 02:37:28 2016 -0700
profiler: extract SpProfiler into an interface
It would be nice to be able to support a remote profiler session in the
future, so add SpLocalProfiler implementation of SpProfiler interface.
lib/Makefile.am | 2 +
lib/sp-local-profiler.c | 807 ++++++++++++++++++++++++++++++++++++++
lib/sp-local-profiler.h | 40 ++
lib/sp-profiler.c | 1000 ++++++++---------------------------------------
lib/sp-profiler.h | 105 ++++-
lib/sysprof.h | 3 +-
src/sp-window.c | 4 +-
tools/sysprof-cli.c | 2 +-
8 files changed, 1094 insertions(+), 869 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 02956bc..1af68f8 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -56,6 +56,7 @@ headers_DATA = \
sp-jitmap-symbol-resolver.h \
sp-kernel-symbol.h \
sp-kernel-symbol-resolver.h \
+ sp-local-profiler.h \
sp-map-lookaside.h \
sp-perf-source.h \
sp-proc-source.h \
@@ -81,6 +82,7 @@ libsysprof_ API_VERSION@_la_SOURCES = \
sp-kernel-symbol-resolver.c \
sp-line-reader.c \
sp-line-reader.h \
+ sp-local-profiler.c \
sp-map-lookaside.c \
sp-perf-counter.c \
sp-perf-counter.h \
diff --git a/lib/sp-local-profiler.c b/lib/sp-local-profiler.c
new file mode 100644
index 0000000..f3ca162
--- /dev/null
+++ b/lib/sp-local-profiler.c
@@ -0,0 +1,807 @@
+/* sp-local-profiler.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/>.
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <errno.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "sp-local-profiler.h"
+
+typedef struct
+{
+ SpCaptureWriter *writer;
+
+ /* All sources added */
+ GPtrArray *sources;
+
+ /* Array of GError failures */
+ GPtrArray *failures;
+
+ /* Sources currently starting */
+ GPtrArray *starting;
+
+ /* Sources currently stopping */
+ GPtrArray *stopping;
+
+ /* Sources that have failed or finished */
+ GPtrArray *finished_or_failed;
+
+ /* Pids to notify children about before prepare */
+ GArray *pids;
+
+ /* Timer for simple time tracking */
+ GTimer *timer;
+ guint timer_notify_source;
+
+ /* Arguments and environment variables for spawning */
+ gchar **spawn_argv;
+ gchar **spawn_env;
+
+ /* State flags */
+ guint is_running : 1;
+ guint is_stopping : 1;
+ guint is_starting : 1;
+
+ /*
+ * If we should spawn argv when starting up. This allows UI to set
+ * spawn argv/env but enable disable with a toggle.
+ */
+ guint spawn : 1;
+
+ /* If we should inherit the environment when spawning */
+ guint spawn_inherit_environ : 1;
+
+ /*
+ * If we should profile the entire system. Setting this results in pids
+ * being ignored. This is primarily useful for UI to toggle on/off the
+ * feature of per-process vs whole-system.
+ */
+ guint whole_system : 1;
+} SpLocalProfilerPrivate;
+
+static void profiler_iface_init (SpProfilerInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (SpLocalProfiler, sp_local_profiler, G_TYPE_OBJECT, 0,
+ G_ADD_PRIVATE (SpLocalProfiler)
+ G_IMPLEMENT_INTERFACE (SP_TYPE_PROFILER, profiler_iface_init))
+
+enum {
+ PROP_0,
+ N_PROPS,
+
+ PROP_ELAPSED,
+ PROP_IS_MUTABLE,
+ PROP_IS_RUNNING,
+ PROP_SPAWN,
+ PROP_SPAWN_ARGV,
+ PROP_SPAWN_ENV,
+ PROP_SPAWN_INHERIT_ENVIRON,
+ PROP_WHOLE_SYSTEM,
+};
+
+static inline gint
+_g_ptr_array_find (GPtrArray *ar,
+ gpointer item)
+{
+ guint i;
+
+ for (i = 0; i < ar->len; i++)
+ {
+ if (item == g_ptr_array_index (ar, i))
+ return i;
+ }
+
+ return -1;
+}
+
+static inline gboolean
+_g_ptr_array_contains (GPtrArray *ar,
+ gpointer item)
+{
+ return (-1 != _g_ptr_array_find (ar, item));
+}
+
+static void
+sp_local_profiler_clear_timer (SpLocalProfiler *self)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+
+ g_clear_pointer (&priv->timer, g_timer_destroy);
+
+ if (priv->timer_notify_source != 0)
+ {
+ g_source_remove (priv->timer_notify_source);
+ priv->timer_notify_source = 0;
+ }
+}
+
+static void
+sp_local_profiler_real_stopped (SpProfiler *profiler)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+
+ sp_local_profiler_clear_timer (self);
+}
+
+static gboolean
+sp_local_profiler_notify_elapsed_cb (gpointer data)
+{
+ SpLocalProfiler *self = data;
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+
+ g_object_notify (G_OBJECT (self), "elapsed");
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+sp_local_profiler_finish_stopping (SpLocalProfiler *self)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (priv->is_starting == FALSE);
+ g_assert (priv->is_stopping == TRUE);
+ g_assert (priv->stopping->len == 0);
+
+ if (priv->failures->len > 0)
+ {
+ const GError *error = g_ptr_array_index (priv->failures, 0);
+
+ sp_profiler_emit_failed (SP_PROFILER (self), error);
+ }
+
+ priv->is_running = FALSE;
+ priv->is_stopping = FALSE;
+
+ sp_profiler_emit_stopped (SP_PROFILER (self));
+
+ g_object_notify (G_OBJECT (self), "is-mutable");
+ g_object_notify (G_OBJECT (self), "is-running");
+}
+
+static void
+sp_local_profiler_stop (SpProfiler *profiler)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ guint i;
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+
+ if (priv->is_stopping || (!priv->is_starting && !priv->is_running))
+ return;
+
+ priv->is_stopping = TRUE;
+
+ /*
+ * First we add everything to the stopping list, so that we can
+ * be notified of when they have completed. If everything stopped
+ * synchronously, the stopping list will be empty after calling
+ * sp_source_stop() for every source. Otherwise, we need to delay
+ * stopping for a little bit.
+ */
+
+ for (i = 0; i < priv->sources->len; i++)
+ {
+ SpSource *source = g_ptr_array_index (priv->sources, i);
+
+ if (!_g_ptr_array_contains (priv->finished_or_failed, source))
+ g_ptr_array_add (priv->stopping, g_object_ref (source));
+ }
+
+ for (i = 0; i < priv->sources->len; i++)
+ {
+ SpSource *source = g_ptr_array_index (priv->sources, i);
+
+ sp_source_stop (source);
+ }
+
+ if (priv->is_stopping && priv->stopping->len == 0)
+ sp_local_profiler_finish_stopping (self);
+}
+
+
+static void
+sp_local_profiler_dispose (GObject *object)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)object;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ if (priv->is_running || priv->is_starting)
+ {
+ sp_local_profiler_stop (SP_PROFILER (self));
+ return;
+ }
+
+ sp_local_profiler_clear_timer (self);
+
+ G_OBJECT_CLASS (sp_local_profiler_parent_class)->dispose (object);
+}
+
+static void
+sp_local_profiler_finalize (GObject *object)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)object;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_clear_pointer (&priv->writer, sp_capture_writer_unref);
+ g_clear_pointer (&priv->sources, g_ptr_array_unref);
+ g_clear_pointer (&priv->starting, g_ptr_array_unref);
+ g_clear_pointer (&priv->stopping, g_ptr_array_unref);
+ g_clear_pointer (&priv->finished_or_failed, g_ptr_array_unref);
+ g_clear_pointer (&priv->pids, g_array_unref);
+
+ G_OBJECT_CLASS (sp_local_profiler_parent_class)->finalize (object);
+}
+
+static void
+sp_local_profiler_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ SpLocalProfiler *self = SP_LOCAL_PROFILER (object);
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_ELAPSED:
+ g_value_set_double (value, priv->timer ? g_timer_elapsed (priv->timer, NULL) : 0.0);
+ break;
+
+ case PROP_IS_MUTABLE:
+ g_value_set_boolean (value, !(priv->is_starting || priv->is_starting || priv->is_running));
+ break;
+
+ case PROP_IS_RUNNING:
+ g_value_set_boolean (value, priv->is_running);
+ break;
+
+ case PROP_WHOLE_SYSTEM:
+ g_value_set_boolean (value, priv->whole_system);
+ break;
+
+ case PROP_SPAWN:
+ g_value_set_boolean (value, priv->spawn);
+ break;
+
+ case PROP_SPAWN_INHERIT_ENVIRON:
+ g_value_set_boolean (value, priv->spawn_inherit_environ);
+ break;
+
+ case PROP_SPAWN_ARGV:
+ g_value_set_boxed (value, priv->spawn_argv);
+ break;
+
+ case PROP_SPAWN_ENV:
+ g_value_set_boxed (value, priv->spawn_env);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sp_local_profiler_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ SpLocalProfiler *self = SP_LOCAL_PROFILER (object);
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_WHOLE_SYSTEM:
+ priv->whole_system = g_value_get_boolean (value);
+ break;
+
+ case PROP_SPAWN:
+ priv->spawn = g_value_get_boolean (value);
+ break;
+
+ case PROP_SPAWN_INHERIT_ENVIRON:
+ priv->spawn_inherit_environ = g_value_get_boolean (value);
+ break;
+
+ case PROP_SPAWN_ARGV:
+ g_strfreev (priv->spawn_argv);
+ priv->spawn_argv = g_value_dup_boxed (value);
+ break;
+
+ case PROP_SPAWN_ENV:
+ g_strfreev (priv->spawn_env);
+ priv->spawn_env = g_value_dup_boxed (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+sp_local_profiler_class_init (SpLocalProfilerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = sp_local_profiler_dispose;
+ object_class->finalize = sp_local_profiler_finalize;
+ object_class->get_property = sp_local_profiler_get_property;
+ object_class->set_property = sp_local_profiler_set_property;
+
+ g_object_class_override_property (object_class, PROP_ELAPSED, "elapsed");
+ g_object_class_override_property (object_class, PROP_IS_MUTABLE, "is-mutable");
+ g_object_class_override_property (object_class, PROP_IS_RUNNING, "is-running");
+ g_object_class_override_property (object_class, PROP_SPAWN, "spawn");
+ g_object_class_override_property (object_class, PROP_SPAWN_ARGV, "spawn-argv");
+ g_object_class_override_property (object_class, PROP_SPAWN_ENV, "spawn-env");
+ g_object_class_override_property (object_class, PROP_SPAWN_INHERIT_ENVIRON, "spawn-inherit-environ");
+ g_object_class_override_property (object_class, PROP_WHOLE_SYSTEM, "whole-system");
+}
+
+static void
+sp_local_profiler_init (SpLocalProfiler *self)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ priv->whole_system = TRUE;
+
+ priv->failures = g_ptr_array_new_with_free_func ((GDestroyNotify)g_error_free);
+ priv->sources = g_ptr_array_new_with_free_func (g_object_unref);
+ priv->starting = g_ptr_array_new_with_free_func (g_object_unref);
+ priv->stopping = g_ptr_array_new_with_free_func (g_object_unref);
+ priv->finished_or_failed = g_ptr_array_new_with_free_func (g_object_unref);
+ priv->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
+}
+
+SpProfiler *
+sp_local_profiler_new (void)
+{
+ return g_object_new (SP_TYPE_LOCAL_PROFILER, NULL);
+}
+
+static void
+sp_local_profiler_finish_startup (SpLocalProfiler *self)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ guint i;
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (priv->is_starting == TRUE);
+ g_assert (priv->starting->len == 0);
+
+ sp_local_profiler_clear_timer (self);
+
+ priv->timer = g_timer_new ();
+
+ /*
+ * Add a source to update our watchers of elapsed time.
+ * We use 1000 instead of add_seconds(1) so that we are
+ * not subject to as much drift.
+ */
+ priv->timer_notify_source =
+ g_timeout_add (1000,
+ sp_local_profiler_notify_elapsed_cb,
+ self);
+
+ for (i = 0; i < priv->sources->len; i++)
+ {
+ SpSource *source = g_ptr_array_index (priv->sources, i);
+
+ sp_source_start (source);
+ }
+
+ priv->is_starting = FALSE;
+
+ /*
+ * If any of the sources failed during startup, we will have a non-empty
+ * failures list.
+ */
+ if (priv->failures->len > 0)
+ {
+ const GError *error = g_ptr_array_index (priv->failures, 0);
+
+ g_object_ref (self);
+ sp_profiler_emit_failed (SP_PROFILER (self), error);
+ sp_local_profiler_stop (SP_PROFILER (self));
+ g_object_unref (self);
+ return;
+ }
+
+ priv->is_running = TRUE;
+
+ g_object_notify (G_OBJECT (self), "is-mutable");
+ g_object_notify (G_OBJECT (self), "is-running");
+
+ /*
+ * If all the sources are transient (in that they just generate information
+ * and then exit), we could be finished as soon as we complete startup.
+ *
+ * If we detect this, we stop immediately.
+ */
+ if (priv->finished_or_failed->len == priv->sources->len)
+ sp_local_profiler_stop (SP_PROFILER (self));
+}
+
+static void
+sp_local_profiler_start (SpProfiler *profiler)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ guint i;
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+ g_return_if_fail (priv->is_running == FALSE);
+ g_return_if_fail (priv->is_stopping == FALSE);
+ g_return_if_fail (priv->is_starting == FALSE);
+
+ if (priv->writer == NULL)
+ {
+ SpCaptureWriter *writer;
+ int fd;
+
+ if ((-1 == (fd = syscall (__NR_memfd_create, "[sysprof]", 0))) ||
+ (NULL == (writer = sp_capture_writer_new_from_fd (fd, 0))))
+ {
+ const GError error = {
+ G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ (gchar *)g_strerror (errno)
+ };
+
+ if (fd != -1)
+ close (fd);
+
+ sp_profiler_emit_failed (SP_PROFILER (self), &error);
+
+ return;
+ }
+
+ sp_profiler_set_writer (SP_PROFILER (self), writer);
+ g_clear_pointer (&writer, sp_capture_writer_unref);
+ }
+
+ priv->is_running = TRUE;
+ priv->is_starting = TRUE;
+
+ if (priv->failures->len > 0)
+ g_ptr_array_remove_range (priv->failures, 0, priv->failures->len);
+
+ if (priv->spawn && priv->spawn_argv && priv->spawn_argv[0])
+ {
+ g_autoptr(GPtrArray) ar = g_ptr_array_new_with_free_func (g_free);
+ GPid pid;
+ GError *error = NULL;
+
+ if (priv->spawn_inherit_environ)
+ {
+ gchar **environ = g_get_environ ();
+
+ for (i = 0; environ[i]; i++)
+ g_ptr_array_add (ar, environ[i]);
+ g_free (environ);
+ }
+
+ if (priv->spawn_env)
+ {
+ for (i = 0; priv->spawn_env[i]; i++)
+ g_ptr_array_add (ar, g_strdup (priv->spawn_env[i]));
+ }
+
+ g_ptr_array_add (ar, NULL);
+
+ if (!g_spawn_async (g_get_home_dir (),
+ priv->spawn_argv,
+ (gchar **)ar->pdata,
+ (G_SPAWN_SEARCH_PATH |
+ G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDOUT_TO_DEV_NULL),
+ NULL,
+ NULL,
+ &pid,
+ &error))
+ g_ptr_array_add (priv->failures, error);
+ else
+ g_array_append_val (priv->pids, pid);
+ }
+
+ for (i = 0; i < priv->sources->len; i++)
+ {
+ SpSource *source = g_ptr_array_index (priv->sources, i);
+ guint j;
+
+ if (priv->whole_system == FALSE)
+ {
+ for (j = 0; j < priv->pids->len; j++)
+ {
+ GPid pid = g_array_index (priv->pids, GPid, j);
+
+ sp_source_add_pid (source, pid);
+ }
+ }
+
+ sp_source_set_writer (source, priv->writer);
+ sp_source_prepare (source);
+ }
+
+ for (i = 0; i < priv->sources->len; i++)
+ {
+ SpSource *source = g_ptr_array_index (priv->sources, i);
+
+ if (!sp_source_get_is_ready (source))
+ g_ptr_array_add (priv->starting, g_object_ref (source));
+ }
+
+ if (priv->starting->len == 0)
+ sp_local_profiler_finish_startup (self);
+}
+
+static void
+sp_local_profiler_set_writer (SpProfiler *profiler,
+ SpCaptureWriter *writer)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+ g_return_if_fail (priv->is_running == FALSE);
+ g_return_if_fail (priv->is_stopping == FALSE);
+ g_return_if_fail (writer != NULL);
+
+ if (priv->writer != writer)
+ {
+ g_clear_pointer (&priv->writer, sp_capture_writer_unref);
+
+ if (writer != NULL)
+ priv->writer = sp_capture_writer_ref (writer);
+ }
+}
+
+static void
+sp_local_profiler_track_completed (SpLocalProfiler *self,
+ SpSource *source)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ gint i;
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (SP_IS_SOURCE (source));
+
+ if (!_g_ptr_array_contains (priv->finished_or_failed, source))
+ g_ptr_array_add (priv->finished_or_failed, g_object_ref (source));
+
+ if (priv->is_starting)
+ {
+ i = _g_ptr_array_find (priv->starting, source);
+
+ if (i >= 0)
+ {
+ g_ptr_array_remove_index (priv->starting, i);
+ if (priv->starting->len == 0)
+ sp_local_profiler_finish_startup (self);
+ }
+ }
+
+ if (priv->is_stopping)
+ {
+ i = _g_ptr_array_find (priv->stopping, source);
+
+ if (i >= 0)
+ {
+ g_ptr_array_remove_index_fast (priv->stopping, i);
+
+ if ((priv->is_stopping == TRUE) && (priv->stopping->len == 0))
+ sp_local_profiler_finish_stopping (self);
+ }
+ }
+
+ if (!priv->is_starting)
+ {
+ if (priv->finished_or_failed->len == priv->sources->len)
+ sp_local_profiler_stop (SP_PROFILER (self));
+ }
+}
+
+static void
+sp_local_profiler_source_finished (SpLocalProfiler *self,
+ SpSource *source)
+{
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (SP_IS_SOURCE (source));
+
+ sp_local_profiler_track_completed (self, source);
+}
+
+static void
+sp_local_profiler_source_ready (SpLocalProfiler *self,
+ SpSource *source)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ guint i;
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (SP_IS_SOURCE (source));
+
+ for (i = 0; i < priv->starting->len; i++)
+ {
+ SpSource *ele = g_ptr_array_index (priv->starting, i);
+
+ if (ele == source)
+ {
+ g_ptr_array_remove_index_fast (priv->starting, i);
+
+ if ((priv->is_starting == TRUE) && (priv->starting->len == 0))
+ sp_local_profiler_finish_startup (self);
+
+ break;
+ }
+ }
+}
+
+static void
+sp_local_profiler_source_failed (SpLocalProfiler *self,
+ const GError *reason,
+ SpSource *source)
+{
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_assert (SP_IS_LOCAL_PROFILER (self));
+ g_assert (reason != NULL);
+ g_assert (SP_IS_SOURCE (source));
+
+ sp_local_profiler_track_completed (self, source);
+
+ /* Failure emitted out of band */
+ if (!priv->is_starting && !priv->is_stopping && !priv->is_running)
+ return;
+
+ g_ptr_array_add (priv->failures, g_error_copy (reason));
+
+ /* Ignore during start/stop, we handle this in other places */
+ if (priv->is_starting || priv->is_stopping)
+ return;
+
+ if (priv->is_running)
+ sp_local_profiler_stop (SP_PROFILER (self));
+}
+
+static void
+sp_local_profiler_add_source (SpProfiler *profiler,
+ SpSource *source)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+ g_return_if_fail (SP_IS_SOURCE (source));
+ g_return_if_fail (priv->is_running == FALSE);
+ g_return_if_fail (priv->is_starting == FALSE);
+ g_return_if_fail (priv->is_stopping == FALSE);
+
+ g_signal_connect_object (source,
+ "failed",
+ G_CALLBACK (sp_local_profiler_source_failed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (source,
+ "finished",
+ G_CALLBACK (sp_local_profiler_source_finished),
+ self,
+ G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (source,
+ "ready",
+ G_CALLBACK (sp_local_profiler_source_ready),
+ self,
+ G_CONNECT_SWAPPED);
+
+
+ g_ptr_array_add (priv->sources, g_object_ref (source));
+}
+
+static void
+sp_local_profiler_add_pid (SpProfiler *profiler,
+ GPid pid)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+ g_return_if_fail (pid > -1);
+ g_return_if_fail (priv->is_starting == FALSE);
+ g_return_if_fail (priv->is_stopping == FALSE);
+ g_return_if_fail (priv->is_running == FALSE);
+
+ g_array_append_val (priv->pids, pid);
+}
+
+static void
+sp_local_profiler_remove_pid (SpProfiler *profiler,
+ GPid pid)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+ guint i;
+
+ g_return_if_fail (SP_IS_LOCAL_PROFILER (self));
+ g_return_if_fail (pid > -1);
+ g_return_if_fail (priv->is_starting == FALSE);
+ g_return_if_fail (priv->is_stopping == FALSE);
+ g_return_if_fail (priv->is_running == FALSE);
+
+ for (i = 0; i < priv->pids->len; i++)
+ {
+ GPid ele = g_array_index (priv->pids, GPid, i);
+
+ if (ele == pid)
+ {
+ g_array_remove_index_fast (priv->pids, i);
+ break;
+ }
+ }
+}
+
+static const GPid *
+sp_local_profiler_get_pids (SpProfiler *profiler,
+ guint *n_pids)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_return_val_if_fail (SP_IS_LOCAL_PROFILER (self), NULL);
+ g_return_val_if_fail (n_pids != NULL, NULL);
+
+ *n_pids = priv->pids->len;
+
+ return (GPid *)(gpointer)priv->pids->data;
+}
+
+static SpCaptureWriter *
+sp_local_profiler_get_writer (SpProfiler *profiler)
+{
+ SpLocalProfiler *self = (SpLocalProfiler *)profiler;
+ SpLocalProfilerPrivate *priv = sp_local_profiler_get_instance_private (self);
+
+ g_return_val_if_fail (SP_IS_LOCAL_PROFILER (self), NULL);
+
+ return priv->writer;
+}
+
+static void
+profiler_iface_init (SpProfilerInterface *iface)
+{
+ iface->add_pid = sp_local_profiler_add_pid;
+ iface->add_source = sp_local_profiler_add_source;
+ iface->get_pids = sp_local_profiler_get_pids;
+ iface->get_writer = sp_local_profiler_get_writer;
+ iface->remove_pid = sp_local_profiler_remove_pid;
+ iface->set_writer = sp_local_profiler_set_writer;
+ iface->start = sp_local_profiler_start;
+ iface->stop = sp_local_profiler_stop;
+ iface->stopped = sp_local_profiler_real_stopped;
+}
diff --git a/lib/sp-local-profiler.h b/lib/sp-local-profiler.h
new file mode 100644
index 0000000..0beae16
--- /dev/null
+++ b/lib/sp-local-profiler.h
@@ -0,0 +1,40 @@
+/* sp-local-profiler.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_LOCAL_PROFILER_H
+#define SP_LOCAL_PROFILER_H
+
+#include "sp-profiler.h"
+
+G_BEGIN_DECLS
+
+#define SP_TYPE_LOCAL_PROFILER (sp_local_profiler_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (SpLocalProfiler, sp_local_profiler, SP, LOCAL_PROFILER, GObject)
+
+struct _SpLocalProfilerClass
+{
+ GObjectClass parent_class;
+ gpointer padding[8];
+};
+
+SpProfiler *sp_local_profiler_new (void);
+
+G_END_DECLS
+
+#endif /* SP_LOCAL_PROFILER_H */
diff --git a/lib/sp-profiler.c b/lib/sp-profiler.c
index 73b6388..7482f73 100644
--- a/lib/sp-profiler.c
+++ b/lib/sp-profiler.c
@@ -16,78 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <errno.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
#include "sp-profiler.h"
-typedef struct
-{
- SpCaptureWriter *writer;
-
- /* All sources added */
- GPtrArray *sources;
-
- /* Array of GError failures */
- GPtrArray *failures;
-
- /* Sources currently starting */
- GPtrArray *starting;
-
- /* Sources currently stopping */
- GPtrArray *stopping;
-
- /* Sources that have failed or finished */
- GPtrArray *finished_or_failed;
-
- /* Pids to notify children about before prepare */
- GArray *pids;
-
- /* Timer for simple time tracking */
- GTimer *timer;
- guint timer_notify_source;
-
- /* Arguments and environment variables for spawning */
- gchar **spawn_argv;
- gchar **spawn_env;
-
- /* State flags */
- guint is_running : 1;
- guint is_stopping : 1;
- guint is_starting : 1;
-
- /*
- * If we should spawn argv when starting up. This allows UI to set
- * spawn argv/env but enable disable with a toggle.
- */
- guint spawn : 1;
-
- /* If we should inherit the environment when spawning */
- guint spawn_inherit_environ : 1;
-
- /*
- * If we should profile the entire system. Setting this results in pids
- * being ignored. This is primarily useful for UI to toggle on/off the
- * feature of per-process vs whole-system.
- */
- guint whole_system : 1;
-} SpProfilerPrivate;
-
-G_DEFINE_TYPE_WITH_PRIVATE (SpProfiler, sp_profiler, G_TYPE_OBJECT)
-
-enum {
- PROP_0,
- PROP_IS_MUTABLE,
- PROP_IS_RUNNING,
- PROP_ELAPSED,
- PROP_SPAWN,
- PROP_SPAWN_INHERIT_ENVIRON,
- PROP_WHOLE_SYSTEM,
- N_PROPS
-};
+G_DEFINE_INTERFACE (SpProfiler, sp_profiler, G_TYPE_OBJECT)
enum {
FAILED,
@@ -95,889 +26,266 @@ enum {
N_SIGNALS
};
-static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
-static inline gint
-_g_ptr_array_find (GPtrArray *ar,
- gpointer item)
+static void
+sp_profiler_default_init (SpProfilerInterface *iface)
{
- guint i;
-
- for (i = 0; i < ar->len; i++)
- {
- if (item == g_ptr_array_index (ar, i))
- return i;
- }
-
- return -1;
-}
+ signals [FAILED] = g_signal_new ("failed",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SpProfilerInterface, failed),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_ERROR);
-static inline gboolean
-_g_ptr_array_contains (GPtrArray *ar,
- gpointer item)
-{
- return (-1 != _g_ptr_array_find (ar, item));
+ signals [STOPPED] = g_signal_new ("stopped",
+ G_TYPE_FROM_INTERFACE (iface),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (SpProfilerInterface, stopped),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ g_object_interface_install_property (iface,
+ g_param_spec_double ("elapsed",
+ "Elapsed",
+ "The amount of elapsed time profiling",
+ 0,
+ G_MAXDOUBLE,
+ 0,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("is-running",
+ "Is Running",
+ "If the profiler is currently running.",
+ FALSE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("is-mutable",
+ "Is Mutable",
+ "If the profiler can still be prepared.",
+ TRUE,
+ (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("spawn-inherit-environ",
+ "Spawn Inherit Environ",
+ "If the spawned child should inherit the parents environment",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("whole-system",
+ "Whole System",
+ "If the whole system should be profiled",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boolean ("spawn",
+ "Spawn",
+ "If configured child should be spawned",
+ TRUE,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boxed ("spawn-argv",
+ "Spawn Argv",
+ "The arguments for the spawn child",
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
+
+ g_object_interface_install_property (iface,
+ g_param_spec_boxed ("spawn-env",
+ "Spawn Environment",
+ "The environment for the spawn child",
+ G_TYPE_STRV,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}
gdouble
sp_profiler_get_elapsed (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
+ gdouble value = 0.0;
g_return_val_if_fail (SP_IS_PROFILER (self), 0.0);
-
- return (priv->timer != NULL) ? g_timer_elapsed (priv->timer, NULL) : 0.0;
-}
-
-static void
-sp_profiler_clear_timer (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_assert (SP_IS_PROFILER (self));
-
- g_clear_pointer (&priv->timer, g_timer_destroy);
-
- if (priv->timer_notify_source != 0)
- {
- g_source_remove (priv->timer_notify_source);
- priv->timer_notify_source = 0;
- }
-}
-
-static void
-sp_profiler_real_stopped (SpProfiler *self)
-{
- g_assert (SP_IS_PROFILER (self));
-
- sp_profiler_clear_timer (self);
-}
-
-static gboolean
-sp_profiler_notify_elapsed_cb (gpointer data)
-{
- SpProfiler *self = data;
-
- g_assert (SP_IS_PROFILER (self));
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ELAPSED]);
-
- return G_SOURCE_CONTINUE;
-}
-
-static void
-sp_profiler_dispose (GObject *object)
-{
- SpProfiler *self = (SpProfiler *)object;
-
- if (sp_profiler_get_is_running (self))
- {
- sp_profiler_stop (self);
- return;
- }
-
- sp_profiler_clear_timer (self);
-
- G_OBJECT_CLASS (sp_profiler_parent_class)->dispose (object);
+ g_object_get (self, "elapsed", &value, NULL);
+ return value;
}
-static void
-sp_profiler_finalize (GObject *object)
+gboolean
+sp_profiler_get_is_running (SpProfiler *self)
{
- SpProfiler *self = (SpProfiler *)object;
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_clear_pointer (&priv->writer, sp_capture_writer_unref);
- g_clear_pointer (&priv->sources, g_ptr_array_unref);
- g_clear_pointer (&priv->starting, g_ptr_array_unref);
- g_clear_pointer (&priv->stopping, g_ptr_array_unref);
- g_clear_pointer (&priv->finished_or_failed, g_ptr_array_unref);
- g_clear_pointer (&priv->pids, g_array_unref);
-
- G_OBJECT_CLASS (sp_profiler_parent_class)->finalize (object);
+ gboolean is_running = FALSE;
+ g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_object_get (self, "is-running", &is_running, NULL);
+ return is_running;
}
-static void
-sp_profiler_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+gboolean
+sp_profiler_get_is_mutable (SpProfiler *self)
{
- SpProfiler *self = SP_PROFILER (object);
-
- switch (prop_id)
- {
- case PROP_IS_MUTABLE:
- g_value_set_boolean (value, sp_profiler_get_is_mutable (self));
- break;
-
- case PROP_IS_RUNNING:
- g_value_set_boolean (value, sp_profiler_get_is_running (self));
- break;
-
- case PROP_WHOLE_SYSTEM:
- g_value_set_boolean (value, sp_profiler_get_whole_system (self));
- break;
-
- case PROP_SPAWN:
- g_value_set_boolean (value, sp_profiler_get_spawn (self));
- break;
-
- case PROP_SPAWN_INHERIT_ENVIRON:
- g_value_set_boolean (value, sp_profiler_get_spawn_inherit_environ (self));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
+ gboolean is_mutable = FALSE;
+ g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_object_get (self, "is-mutable", &is_mutable, NULL);
+ return is_mutable;
}
-static void
-sp_profiler_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+gboolean
+sp_profiler_get_spawn_inherit_environ (SpProfiler *self)
{
- SpProfiler *self = SP_PROFILER (object);
-
- switch (prop_id)
- {
- case PROP_WHOLE_SYSTEM:
- sp_profiler_set_whole_system (self, g_value_get_boolean (value));
- break;
-
- case PROP_SPAWN:
- sp_profiler_set_spawn (self, g_value_get_boolean (value));
- break;
-
- case PROP_SPAWN_INHERIT_ENVIRON:
- sp_profiler_set_spawn_inherit_environ (self, g_value_get_boolean (value));
- break;
-
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- }
+ gboolean spawn_inherit_environ = FALSE;
+ g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_object_get (self, "spawn-inherit-environ", &spawn_inherit_environ, NULL);
+ return spawn_inherit_environ;
}
-static void
-sp_profiler_class_init (SpProfilerClass *klass)
+void
+sp_profiler_set_spawn_inherit_environ (SpProfiler *self,
+ gboolean spawn_inherit_environ)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->dispose = sp_profiler_dispose;
- object_class->finalize = sp_profiler_finalize;
- object_class->get_property = sp_profiler_get_property;
- object_class->set_property = sp_profiler_set_property;
-
- klass->stopped = sp_profiler_real_stopped;
-
- /**
- * SpProfiler:elapsed:
- *
- * This property is updated on a second basis while recording so that
- * UIs can keep a timer of the elapsed time while recording.
- *
- * It contains a double with seconds as whole integers and fractions
- * of second after the decimal point.
- */
- properties [PROP_ELAPSED] =
- g_param_spec_double ("elapsed",
- "Elapsed Time",
- "The amount of time elapsed while recording",
- 0.0,
- G_MAXDOUBLE,
- 0.0,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * SpProfiler:is-running:
- *
- * If the profiler has been started. Note that after being started, this
- * property won't change back to %FALSE until all sources have stopped
- * and notified of asynchronous completion.
- */
- properties [PROP_IS_RUNNING] =
- g_param_spec_boolean ("is-running",
- "Is Running",
- "If the profiler has been started",
- FALSE,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * SpProfiler:is-mutable:
- *
- * This property is useful from a UI standpoint to desensitize
- * configuration widgets once the profiler can no longer be modified.
- */
- properties [PROP_IS_MUTABLE] =
- g_param_spec_boolean ("is-mutable",
- "Is Mutable",
- "If the profiler can be modified",
- FALSE,
- (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_SPAWN] =
- g_param_spec_boolean ("spawn",
- "Spawn",
- "If a child should process should be spawned",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- properties [PROP_SPAWN_INHERIT_ENVIRON] =
- g_param_spec_boolean ("spawn-inherit-environ",
- "Spawn Inherit Environ",
- "If a child should inherit the current environment",
- FALSE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- /**
- * SpProfiler:whole-system:
- *
- * This property denotes if the whole system should be profiled instead of
- * a single process. This is useful for UI to toggle between process
- * selection and all processes.
- *
- * Setting this to %TRUE will result in the pids added to be ignored
- * during startup.
- */
- properties [PROP_WHOLE_SYSTEM] =
- g_param_spec_boolean ("whole-system",
- "Whole System",
- "If the whole system should be profiled",
- TRUE,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_properties (object_class, N_PROPS, properties);
-
- signals [STOPPED] = g_signal_new ("stopped",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (SpProfilerClass, stopped),
- NULL, NULL, NULL, G_TYPE_NONE, 0);
-
- signals [FAILED] = g_signal_new ("failed",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (SpProfilerClass, failed),
- NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR);
-
+ g_return_if_fail (SP_IS_PROFILER (self));
+ g_object_set (self, "spawn-inherit-environ", !!spawn_inherit_environ, NULL);
}
-static void
-sp_profiler_init (SpProfiler *self)
+gboolean
+sp_profiler_get_spawn (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- priv->whole_system = TRUE;
-
- priv->failures = g_ptr_array_new_with_free_func ((GDestroyNotify)g_error_free);
- priv->sources = g_ptr_array_new_with_free_func (g_object_unref);
- priv->starting = g_ptr_array_new_with_free_func (g_object_unref);
- priv->stopping = g_ptr_array_new_with_free_func (g_object_unref);
- priv->finished_or_failed = g_ptr_array_new_with_free_func (g_object_unref);
- priv->pids = g_array_new (FALSE, FALSE, sizeof (GPid));
+ gboolean spawn = FALSE;
+ g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_object_get (self, "spawn", &spawn, NULL);
+ return spawn;
}
-SpProfiler *
-sp_profiler_new (void)
+void
+sp_profiler_set_spawn (SpProfiler *self,
+ gboolean spawn)
{
- return g_object_new (SP_TYPE_PROFILER, NULL);
+ g_return_if_fail (SP_IS_PROFILER (self));
+ g_object_set (self, "spawn", !!spawn, NULL);
}
-static void
-sp_profiler_finish_startup (SpProfiler *self)
+gboolean
+sp_profiler_get_whole_system (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- guint i;
-
- g_assert (SP_IS_PROFILER (self));
- g_assert (priv->is_starting == TRUE);
- g_assert (priv->starting->len == 0);
-
- sp_profiler_clear_timer (self);
-
- priv->timer = g_timer_new ();
-
- /*
- * Add a source to update our watchers of elapsed time.
- * We use 1000 instead of add_seconds(1) so that we are
- * not subject to as much drift.
- */
- priv->timer_notify_source =
- g_timeout_add (1000,
- sp_profiler_notify_elapsed_cb,
- self);
-
- for (i = 0; i < priv->sources->len; i++)
- {
- SpSource *source = g_ptr_array_index (priv->sources, i);
-
- sp_source_start (source);
- }
-
- priv->is_starting = FALSE;
-
- /*
- * If any of the sources failed during startup, we will have a non-empty
- * failures list.
- */
- if (priv->failures->len > 0)
- {
- const GError *error = g_ptr_array_index (priv->failures, 0);
-
- g_object_ref (self);
- g_signal_emit (self, signals [FAILED], 0, error);
- sp_profiler_stop (self);
- g_object_unref (self);
- return;
- }
-
- priv->is_running = TRUE;
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_RUNNING]);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_MUTABLE]);
-
- /*
- * If all the sources are transient (in that they just generate information
- * and then exit), we could be finished as soon as we complete startup.
- *
- * If we detect this, we stop immediately.
- */
- if (priv->finished_or_failed->len == priv->sources->len)
- sp_profiler_stop (self);
+ gboolean whole_system = FALSE;
+ g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_object_get (self, "whole-system", &whole_system, NULL);
+ return whole_system;
}
void
-sp_profiler_start (SpProfiler *self)
+sp_profiler_set_whole_system (SpProfiler *self,
+ gboolean whole_system)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- guint i;
-
g_return_if_fail (SP_IS_PROFILER (self));
- g_return_if_fail (priv->is_running == FALSE);
- g_return_if_fail (priv->is_stopping == FALSE);
- g_return_if_fail (priv->is_starting == FALSE);
-
- if (priv->writer == NULL)
- {
- SpCaptureWriter *writer;
- int fd;
-
- if ((-1 == (fd = syscall (__NR_memfd_create, "[sysprof]", 0))) ||
- (NULL == (writer = sp_capture_writer_new_from_fd (fd, 0))))
- {
- const GError error = {
- G_FILE_ERROR,
- g_file_error_from_errno (errno),
- (gchar *)g_strerror (errno)
- };
-
- if (fd != -1)
- close (fd);
-
- g_signal_emit (self, signals [FAILED], 0, &error);
-
- return;
- }
-
- sp_profiler_set_writer (self, writer);
- g_clear_pointer (&writer, sp_capture_writer_unref);
- }
-
- priv->is_running = TRUE;
- priv->is_starting = TRUE;
-
- if (priv->failures->len > 0)
- g_ptr_array_remove_range (priv->failures, 0, priv->failures->len);
-
- if (priv->spawn && priv->spawn_argv && priv->spawn_argv[0])
- {
- g_autoptr(GPtrArray) ar = g_ptr_array_new_with_free_func (g_free);
- GPid pid;
- GError *error = NULL;
-
- if (priv->spawn_inherit_environ)
- {
- gchar **environ = g_get_environ ();
-
- for (i = 0; environ[i]; i++)
- g_ptr_array_add (ar, environ[i]);
- g_free (environ);
- }
-
- if (priv->spawn_env)
- {
- for (i = 0; priv->spawn_env[i]; i++)
- g_ptr_array_add (ar, g_strdup (priv->spawn_env[i]));
- }
-
- g_ptr_array_add (ar, NULL);
-
- if (!g_spawn_async (g_get_home_dir (),
- priv->spawn_argv,
- (gchar **)ar->pdata,
- (G_SPAWN_SEARCH_PATH |
- G_SPAWN_STDOUT_TO_DEV_NULL |
- G_SPAWN_STDOUT_TO_DEV_NULL),
- NULL,
- NULL,
- &pid,
- &error))
- g_ptr_array_add (priv->failures, error);
- else
- g_array_append_val (priv->pids, pid);
- }
-
- for (i = 0; i < priv->sources->len; i++)
- {
- SpSource *source = g_ptr_array_index (priv->sources, i);
- guint j;
-
- if (priv->whole_system == FALSE)
- {
- for (j = 0; j < priv->pids->len; j++)
- {
- GPid pid = g_array_index (priv->pids, GPid, j);
-
- sp_source_add_pid (source, pid);
- }
- }
-
- sp_source_set_writer (source, priv->writer);
- sp_source_prepare (source);
- }
-
- for (i = 0; i < priv->sources->len; i++)
- {
- SpSource *source = g_ptr_array_index (priv->sources, i);
-
- if (!sp_source_get_is_ready (source))
- g_ptr_array_add (priv->starting, g_object_ref (source));
- }
-
- if (priv->starting->len == 0)
- sp_profiler_finish_startup (self);
+ g_object_set (self, "whole-system", !!whole_system, NULL);
}
-static void
-sp_profiler_finish_stopping (SpProfiler *self)
+void
+sp_profiler_set_spawn_argv (SpProfiler *self,
+ const gchar * const *spawn_argv)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_assert (SP_IS_PROFILER (self));
- g_assert (priv->is_starting == FALSE);
- g_assert (priv->is_stopping == TRUE);
- g_assert (priv->stopping->len == 0);
-
- if (priv->failures->len > 0)
- {
- const GError *error = g_ptr_array_index (priv->failures, 0);
-
- g_signal_emit (self, signals [FAILED], 0, error);
- }
-
- priv->is_running = FALSE;
- priv->is_stopping = FALSE;
-
- g_signal_emit (self, signals [STOPPED], 0);
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_RUNNING]);
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_IS_MUTABLE]);
+ g_return_if_fail (SP_IS_PROFILER (self));
+ g_object_set (self, "spawn-argv", spawn_argv, NULL);
}
void
-sp_profiler_stop (SpProfiler *self)
+sp_profiler_set_spawn_env (SpProfiler *self,
+ const gchar * const *spawn_env)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- guint i;
-
g_return_if_fail (SP_IS_PROFILER (self));
-
- if (priv->is_stopping || (!priv->is_starting && !priv->is_running))
- return;
-
- priv->is_stopping = TRUE;
-
- /*
- * First we add everything to the stopping list, so that we can
- * be notified of when they have completed. If everything stopped
- * synchronously, the stopping list will be empty after calling
- * sp_source_stop() for every source. Otherwise, we need to delay
- * stopping for a little bit.
- */
-
- for (i = 0; i < priv->sources->len; i++)
- {
- SpSource *source = g_ptr_array_index (priv->sources, i);
-
- if (!_g_ptr_array_contains (priv->finished_or_failed, source))
- g_ptr_array_add (priv->stopping, g_object_ref (source));
- }
-
- for (i = 0; i < priv->sources->len; i++)
- {
- SpSource *source = g_ptr_array_index (priv->sources, i);
-
- sp_source_stop (source);
- }
-
- if (priv->is_stopping && priv->stopping->len == 0)
- sp_profiler_finish_stopping (self);
+ g_object_set (self, "spawn-env", spawn_env, NULL);
}
-gboolean
-sp_profiler_get_is_running (SpProfiler *self)
+void
+sp_profiler_add_source (SpProfiler *self,
+ SpSource *source)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
+ g_return_if_fail (SP_IS_PROFILER (self));
+ g_return_if_fail (SP_IS_SOURCE (source));
- return priv->is_running;
+ SP_PROFILER_GET_IFACE (self)->add_source (self, source);
}
void
sp_profiler_set_writer (SpProfiler *self,
SpCaptureWriter *writer)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_if_fail (SP_IS_PROFILER (self));
- g_return_if_fail (priv->is_running == FALSE);
- g_return_if_fail (priv->is_stopping == FALSE);
g_return_if_fail (writer != NULL);
- if (priv->writer != writer)
- {
- g_clear_pointer (&priv->writer, sp_capture_writer_unref);
-
- if (writer != NULL)
- priv->writer = sp_capture_writer_ref (writer);
- }
-}
-
-static void
-sp_profiler_track_completed (SpProfiler *self,
- SpSource *source)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- gint i;
-
- g_assert (SP_IS_PROFILER (self));
- g_assert (SP_IS_SOURCE (source));
-
- if (!_g_ptr_array_contains (priv->finished_or_failed, source))
- g_ptr_array_add (priv->finished_or_failed, g_object_ref (source));
-
- if (priv->is_starting)
- {
- i = _g_ptr_array_find (priv->starting, source);
-
- if (i >= 0)
- {
- g_ptr_array_remove_index (priv->starting, i);
- if (priv->starting->len == 0)
- sp_profiler_finish_startup (self);
- }
- }
-
- if (priv->is_stopping)
- {
- i = _g_ptr_array_find (priv->stopping, source);
-
- if (i >= 0)
- {
- g_ptr_array_remove_index_fast (priv->stopping, i);
-
- if ((priv->is_stopping == TRUE) && (priv->stopping->len == 0))
- sp_profiler_finish_stopping (self);
- }
- }
-
- if (!priv->is_starting)
- {
- if (priv->finished_or_failed->len == priv->sources->len)
- sp_profiler_stop (self);
- }
-}
-
-static void
-sp_profiler_source_finished (SpProfiler *self,
- SpSource *source)
-{
- g_assert (SP_IS_PROFILER (self));
- g_assert (SP_IS_SOURCE (source));
-
- sp_profiler_track_completed (self, source);
+ SP_PROFILER_GET_IFACE (self)->set_writer (self, writer);
}
-static void
-sp_profiler_source_ready (SpProfiler *self,
- SpSource *source)
+SpCaptureWriter *
+sp_profiler_get_writer (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- guint i;
-
- g_assert (SP_IS_PROFILER (self));
- g_assert (SP_IS_SOURCE (source));
-
- for (i = 0; i < priv->starting->len; i++)
- {
- SpSource *ele = g_ptr_array_index (priv->starting, i);
-
- if (ele == source)
- {
- g_ptr_array_remove_index_fast (priv->starting, i);
-
- if ((priv->is_starting == TRUE) && (priv->starting->len == 0))
- sp_profiler_finish_startup (self);
+ g_return_val_if_fail (SP_IS_PROFILER (self), NULL);
- break;
- }
- }
+ return SP_PROFILER_GET_IFACE (self)->get_writer (self);
}
-static void
-sp_profiler_source_failed (SpProfiler *self,
- const GError *reason,
- SpSource *source)
+void
+sp_profiler_start (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_assert (SP_IS_PROFILER (self));
- g_assert (reason != NULL);
- g_assert (SP_IS_SOURCE (source));
-
- sp_profiler_track_completed (self, source);
-
- /* Failure emitted out of band */
- if (!priv->is_starting && !priv->is_stopping && !priv->is_running)
- return;
-
- g_ptr_array_add (priv->failures, g_error_copy (reason));
-
- /* Ignore during start/stop, we handle this in other places */
- if (priv->is_starting || priv->is_stopping)
- return;
+ g_return_if_fail (SP_IS_PROFILER (self));
- if (priv->is_running)
- sp_profiler_stop (self);
+ SP_PROFILER_GET_IFACE (self)->start (self);
}
void
-sp_profiler_add_source (SpProfiler *self,
- SpSource *source)
+sp_profiler_stop (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_if_fail (SP_IS_PROFILER (self));
- g_return_if_fail (SP_IS_SOURCE (source));
- g_return_if_fail (priv->is_running == FALSE);
- g_return_if_fail (priv->is_starting == FALSE);
- g_return_if_fail (priv->is_stopping == FALSE);
-
- g_signal_connect_object (source,
- "failed",
- G_CALLBACK (sp_profiler_source_failed),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (source,
- "finished",
- G_CALLBACK (sp_profiler_source_finished),
- self,
- G_CONNECT_SWAPPED);
-
- g_signal_connect_object (source,
- "ready",
- G_CALLBACK (sp_profiler_source_ready),
- self,
- G_CONNECT_SWAPPED);
-
-
- g_ptr_array_add (priv->sources, g_object_ref (source));
+
+ SP_PROFILER_GET_IFACE (self)->stop (self);
}
void
sp_profiler_add_pid (SpProfiler *self,
GPid pid)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_if_fail (SP_IS_PROFILER (self));
g_return_if_fail (pid > -1);
- g_return_if_fail (priv->is_starting == FALSE);
- g_return_if_fail (priv->is_stopping == FALSE);
- g_return_if_fail (priv->is_running == FALSE);
- g_array_append_val (priv->pids, pid);
+ SP_PROFILER_GET_IFACE (self)->add_pid (self, pid);
}
void
sp_profiler_remove_pid (SpProfiler *self,
GPid pid)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
- guint i;
-
g_return_if_fail (SP_IS_PROFILER (self));
g_return_if_fail (pid > -1);
- g_return_if_fail (priv->is_starting == FALSE);
- g_return_if_fail (priv->is_stopping == FALSE);
- g_return_if_fail (priv->is_running == FALSE);
-
- for (i = 0; i < priv->pids->len; i++)
- {
- GPid ele = g_array_index (priv->pids, GPid, i);
-
- if (ele == pid)
- {
- g_array_remove_index_fast (priv->pids, i);
- break;
- }
- }
-}
-
-gboolean
-sp_profiler_get_is_mutable (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
-
- return !(priv->is_starting || priv->is_stopping || priv->is_running);
-}
-
-gboolean
-sp_profiler_get_whole_system (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
-
- return priv->whole_system;
-}
-
-void
-sp_profiler_set_whole_system (SpProfiler *self,
- gboolean whole_system)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_if_fail (SP_IS_PROFILER (self));
- whole_system = !!whole_system;
-
- if (whole_system != priv->whole_system)
- {
- priv->whole_system = whole_system;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_WHOLE_SYSTEM]);
- }
+ SP_PROFILER_GET_IFACE (self)->remove_pid (self, pid);
}
const GPid *
sp_profiler_get_pids (SpProfiler *self,
guint *n_pids)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_val_if_fail (SP_IS_PROFILER (self), NULL);
g_return_val_if_fail (n_pids != NULL, NULL);
- *n_pids = priv->pids->len;
-
- return (GPid *)(gpointer)priv->pids->data;
-}
-
-/**
- * sp_profiler_get_writer:
- *
- * Returns: (nullable) (transfer none): An #SpCaptureWriter or %NULL.
- */
-SpCaptureWriter *
-sp_profiler_get_writer (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), NULL);
-
- return priv->writer;
-}
-
-gboolean
-sp_profiler_get_spawn (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
-
- return priv->spawn;
-}
-
-void
-sp_profiler_set_spawn (SpProfiler *self,
- gboolean spawn)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_if_fail (SP_IS_PROFILER (self));
-
- spawn = !!spawn;
-
- if (priv->spawn != spawn)
- {
- priv->spawn = spawn;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPAWN]);
- }
-}
-
-gboolean
-sp_profiler_get_spawn_inherit_environ (SpProfiler *self)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_val_if_fail (SP_IS_PROFILER (self), FALSE);
-
- return priv->spawn_inherit_environ;
-}
-
-void
-sp_profiler_set_spawn_inherit_environ (SpProfiler *self,
- gboolean spawn_inherit_environ)
-{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
- g_return_if_fail (SP_IS_PROFILER (self));
-
- spawn_inherit_environ = !!spawn_inherit_environ;
-
- if (priv->spawn_inherit_environ != spawn_inherit_environ)
- {
- priv->spawn_inherit_environ = spawn_inherit_environ;
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SPAWN_INHERIT_ENVIRON]);
- }
+ return SP_PROFILER_GET_IFACE (self)->get_pids (self, n_pids);
}
void
-sp_profiler_set_spawn_argv (SpProfiler *self,
- const gchar * const *spawn_argv)
+sp_profiler_emit_failed (SpProfiler *self,
+ const GError *error)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_if_fail (SP_IS_PROFILER (self));
+ g_return_if_fail (error != NULL);
- g_strfreev (priv->spawn_argv);
- priv->spawn_argv = g_strdupv ((gchar **)spawn_argv);
+ g_signal_emit (self, signals [FAILED], 0, error);
}
void
-sp_profiler_set_spawn_env (SpProfiler *self,
- const gchar * const *spawn_env)
+sp_profiler_emit_stopped (SpProfiler *self)
{
- SpProfilerPrivate *priv = sp_profiler_get_instance_private (self);
-
g_return_if_fail (SP_IS_PROFILER (self));
- g_strfreev (priv->spawn_env);
- priv->spawn_env = g_strdupv ((gchar **)spawn_env);
+ g_signal_emit (self, signals [STOPPED], 0);
}
diff --git a/lib/sp-profiler.h b/lib/sp-profiler.h
index 530b6b9..133b8aa 100644
--- a/lib/sp-profiler.h
+++ b/lib/sp-profiler.h
@@ -26,11 +26,11 @@ G_BEGIN_DECLS
#define SP_TYPE_PROFILER (sp_profiler_get_type())
-G_DECLARE_DERIVABLE_TYPE (SpProfiler, sp_profiler, SP, PROFILER, GObject)
+G_DECLARE_INTERFACE (SpProfiler, sp_profiler, SP, PROFILER, GObject)
-struct _SpProfilerClass
+struct _SpProfilerInterface
{
- GObjectClass parent_class;
+ GTypeInterface parent_interface;
/**
* SpProfiler::failed:
@@ -55,11 +55,92 @@ struct _SpProfilerClass
*/
void (*stopped) (SpProfiler *self);
- gpointer padding[8];
+ /**
+ * SpProfiler::add_source:
+ *
+ * Adds a source to the profiler.
+ */
+ void (*add_source) (SpProfiler *profiler,
+ SpSource *source);
+
+ /**
+ * SpProfiler::set_writer:
+ *
+ * Sets the writer to use for the profiler.
+ */
+ void (*set_writer) (SpProfiler *self,
+ SpCaptureWriter *writer);
+
+ /**
+ * SpProfiler::get_writer:
+ *
+ * Gets the writer that is being used to capture.
+ *
+ * Returns: (nullable) (transfer none): A #SpCaptureWriter.
+ */
+ SpCaptureWriter *(*get_writer) (SpProfiler *self);
+
+ /**
+ * SpProfiler::start:
+ *
+ * Starts the profiler.
+ */
+ void (*start) (SpProfiler *self);
+
+ /**
+ * SpProfiler::stop:
+ *
+ * Stops the profiler.
+ */
+ void (*stop) (SpProfiler *self);
+
+ /**
+ * SpProfiler::add_pid:
+ *
+ * Add a pid to be profiled.
+ */
+ void (*add_pid) (SpProfiler *self,
+ GPid pid);
+
+ /**
+ * SpProfiler::remove_pid:
+ *
+ * Remove a pid from the profiler. This will not be called after
+ * SpProfiler::start has been called.
+ */
+ void (*remove_pid) (SpProfiler *self,
+ GPid pid);
+
+ /**
+ * SpProfiler::get_pids:
+ *
+ * Gets the pids that are part of this profiling session. If no pids
+ * have been specified, %NULL is returned.
+ *
+ * Returns: (nullable) (transfer none): An array of #GPid, or %NULL.
+ */
+ const GPid *(*get_pids) (SpProfiler *self,
+ guint *n_pids);
};
-SpProfiler *sp_profiler_new (void);
+void sp_profiler_emit_failed (SpProfiler *self,
+ const GError *error);
+void sp_profiler_emit_stopped (SpProfiler *self);
gdouble sp_profiler_get_elapsed (SpProfiler *self);
+gboolean sp_profiler_get_is_mutable (SpProfiler *self);
+gboolean sp_profiler_get_spawn_inherit_environ (SpProfiler *self);
+void sp_profiler_set_spawn_inherit_environ (SpProfiler *self,
+ gboolean spawn_inherit_environ);
+gboolean sp_profiler_get_whole_system (SpProfiler *self);
+void sp_profiler_set_whole_system (SpProfiler *self,
+ gboolean whole_system);
+gboolean sp_profiler_get_spawn (SpProfiler *self);
+void sp_profiler_set_spawn (SpProfiler *self,
+ gboolean spawn);
+void sp_profiler_set_spawn_argv (SpProfiler *self,
+ const gchar * const *spawn_argv);
+void sp_profiler_set_spawn_env (SpProfiler *self,
+ const gchar * const *spawn_env);
void sp_profiler_add_source (SpProfiler *self,
SpSource *source);
void sp_profiler_set_writer (SpProfiler *self,
@@ -72,22 +153,8 @@ void sp_profiler_add_pid (SpProfiler *sel
GPid pid);
void sp_profiler_remove_pid (SpProfiler *self,
GPid pid);
-gboolean sp_profiler_get_is_mutable (SpProfiler *self);
-gboolean sp_profiler_get_whole_system (SpProfiler *self);
-void sp_profiler_set_whole_system (SpProfiler *self,
- gboolean whole_system);
const GPid *sp_profiler_get_pids (SpProfiler *self,
guint *n_pids);
-gboolean sp_profiler_get_spawn (SpProfiler *self);
-void sp_profiler_set_spawn (SpProfiler *self,
- gboolean spawn);
-void sp_profiler_set_spawn_argv (SpProfiler *self,
- const gchar * const *spawn_argv);
-void sp_profiler_set_spawn_env (SpProfiler *self,
- const gchar * const *spawn_env);
-gboolean sp_profiler_get_spawn_inherit_environ (SpProfiler *self);
-void sp_profiler_set_spawn_inherit_environ (SpProfiler *self,
- gboolean spawn_inherit_environ);
G_END_DECLS
diff --git a/lib/sysprof.h b/lib/sysprof.h
index f864b70..0ff7db7 100644
--- a/lib/sysprof.h
+++ b/lib/sysprof.h
@@ -33,8 +33,9 @@ G_BEGIN_DECLS
# include "sp-error.h"
# include "sp-gjs-source.h"
# include "sp-jitmap-symbol-resolver.h"
-# include "sp-kernel-symbol.h"
# include "sp-kernel-symbol-resolver.h"
+# include "sp-kernel-symbol.h"
+# include "sp-local-profiler.h"
# include "sp-map-lookaside.h"
# include "sp-perf-source.h"
# include "sp-proc-source.h"
diff --git a/src/sp-window.c b/src/sp-window.c
index 06ffbf8..59cf431 100644
--- a/src/sp-window.c
+++ b/src/sp-window.c
@@ -263,7 +263,7 @@ sp_window_set_state (SpWindow *self,
{
case SP_WINDOW_STATE_EMPTY:
case SP_WINDOW_STATE_FAILED:
- profiler = sp_profiler_new ();
+ profiler = sp_local_profiler_new ();
gtk_button_set_label (self->record_button, _("Record"));
gtk_widget_set_sensitive (GTK_WIDGET (self->record_button), TRUE);
@@ -667,7 +667,7 @@ sp_window_constructed (GObject *object)
G_OBJECT_CLASS (sp_window_parent_class)->constructed (object);
- profiler = sp_profiler_new ();
+ profiler = sp_local_profiler_new ();
sp_window_set_profiler (self, profiler);
sp_window_set_state (self, SP_WINDOW_STATE_EMPTY);
diff --git a/tools/sysprof-cli.c b/tools/sysprof-cli.c
index f94ca41..38704e2 100644
--- a/tools/sysprof-cli.c
+++ b/tools/sysprof-cli.c
@@ -128,7 +128,7 @@ main (gint argc,
g_source_add_unix_fd (gsource, efd, G_IO_IN);
g_source_attach (gsource, NULL);
- profiler = sp_profiler_new ();
+ profiler = sp_local_profiler_new ();
g_signal_connect (profiler,
"failed",
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]