From 88d3ae3b742cc0952dbae94985205fb24df124cd Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 14 Apr 2016 02:37:28 -0700 Subject: [PATCH] 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 | 1108 ++++++++------------------------------- lib/sp-profiler.h | 105 +++- lib/sysprof.h | 3 +- src/sp-window.c | 4 +- tools/sysprof-cli.c | 2 +- 8 files changed, 1148 insertions(+), 923 deletions(-) create mode 100644 lib/sp-local-profiler.c create mode 100644 lib/sp-local-profiler.h diff --git a/lib/Makefile.am b/lib/Makefile.am index 02956bc6..1af68f86 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 00000000..f3ca1620 --- /dev/null +++ b/lib/sp-local-profiler.c @@ -0,0 +1,807 @@ +/* sp-local-profiler.c + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#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 00000000..0beae167 --- /dev/null +++ b/lib/sp-local-profiler.h @@ -0,0 +1,40 @@ +/* sp-local-profiler.h + * + * Copyright (C) 2016 Christian Hergert + * + * 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 . + */ + +#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 73b63884..7482f73d 100644 --- a/lib/sp-profiler.c +++ b/lib/sp-profiler.c @@ -16,78 +16,9 @@ * along with this program. If not, see . */ -#include -#include -#include -#include -#include - #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; + 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); - for (i = 0; i < ar->len; i++) - { - if (item == g_ptr_array_index (ar, i)) - return i; - } + 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); - return -1; -} + 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))); -static inline gboolean -_g_ptr_array_contains (GPtrArray *ar, - gpointer item) -{ - return (-1 != _g_ptr_array_find (ar, item)); + 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); -} - -static void -sp_profiler_finalize (GObject *object) -{ - 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); -} - -static void -sp_profiler_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - 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); - } -} - -static void -sp_profiler_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - 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); - } -} - -static void -sp_profiler_class_init (SpProfilerClass *klass) -{ - 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); - -} - -static void -sp_profiler_init (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)); -} - -SpProfiler * -sp_profiler_new (void) -{ - return g_object_new (SP_TYPE_PROFILER, NULL); -} - -static void -sp_profiler_finish_startup (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); -} - -void -sp_profiler_start (SpProfiler *self) -{ - 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); -} - -static void -sp_profiler_finish_stopping (SpProfiler *self) -{ - 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]); -} - -void -sp_profiler_stop (SpProfiler *self) -{ - 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_get (self, "elapsed", &value, NULL); + return value; } gboolean sp_profiler_get_is_running (SpProfiler *self) { - SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); - + gboolean is_running = FALSE; g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); - - return priv->is_running; -} - -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); -} - -static void -sp_profiler_source_ready (SpProfiler *self, - SpSource *source) -{ - 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); - - break; - } - } -} - -static void -sp_profiler_source_failed (SpProfiler *self, - const GError *reason, - SpSource *source) -{ - 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; - - if (priv->is_running) - sp_profiler_stop (self); -} - -void -sp_profiler_add_source (SpProfiler *self, - SpSource *source) -{ - 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)); -} - -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); -} - -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; - } - } + g_object_get (self, "is-running", &is_running, NULL); + return is_running; } gboolean sp_profiler_get_is_mutable (SpProfiler *self) { - SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); - + gboolean is_mutable = FALSE; 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]); - } -} - -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]); - } + g_object_get (self, "is-mutable", &is_mutable, NULL); + return is_mutable; } gboolean sp_profiler_get_spawn_inherit_environ (SpProfiler *self) { - SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); - + gboolean spawn_inherit_environ = FALSE; g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); - - return priv->spawn_inherit_environ; + g_object_get (self, "spawn-inherit-environ", &spawn_inherit_environ, NULL); + return 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)); + g_object_set (self, "spawn-inherit-environ", !!spawn_inherit_environ, NULL); +} - spawn_inherit_environ = !!spawn_inherit_environ; +gboolean +sp_profiler_get_spawn (SpProfiler *self) +{ + gboolean spawn = FALSE; + g_return_val_if_fail (SP_IS_PROFILER (self), FALSE); + g_object_get (self, "spawn", &spawn, NULL); + return spawn; +} - 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]); - } +void +sp_profiler_set_spawn (SpProfiler *self, + gboolean spawn) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_object_set (self, "spawn", !!spawn, NULL); +} + +gboolean +sp_profiler_get_whole_system (SpProfiler *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_set_whole_system (SpProfiler *self, + gboolean whole_system) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_object_set (self, "whole-system", !!whole_system, NULL); } void sp_profiler_set_spawn_argv (SpProfiler *self, const gchar * const *spawn_argv) { - SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); - g_return_if_fail (SP_IS_PROFILER (self)); - - g_strfreev (priv->spawn_argv); - priv->spawn_argv = g_strdupv ((gchar **)spawn_argv); + g_object_set (self, "spawn-argv", spawn_argv, NULL); } void sp_profiler_set_spawn_env (SpProfiler *self, const gchar * const *spawn_env) { - SpProfilerPrivate *priv = sp_profiler_get_instance_private (self); + g_return_if_fail (SP_IS_PROFILER (self)); + g_object_set (self, "spawn-env", spawn_env, NULL); +} +void +sp_profiler_add_source (SpProfiler *self, + SpSource *source) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (SP_IS_SOURCE (source)); + + SP_PROFILER_GET_IFACE (self)->add_source (self, source); +} + +void +sp_profiler_set_writer (SpProfiler *self, + SpCaptureWriter *writer) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (writer != NULL); + + SP_PROFILER_GET_IFACE (self)->set_writer (self, writer); +} + +SpCaptureWriter * +sp_profiler_get_writer (SpProfiler *self) +{ + g_return_val_if_fail (SP_IS_PROFILER (self), NULL); + + return SP_PROFILER_GET_IFACE (self)->get_writer (self); +} + +void +sp_profiler_start (SpProfiler *self) +{ g_return_if_fail (SP_IS_PROFILER (self)); - g_strfreev (priv->spawn_env); - priv->spawn_env = g_strdupv ((gchar **)spawn_env); + SP_PROFILER_GET_IFACE (self)->start (self); +} + +void +sp_profiler_stop (SpProfiler *self) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + + SP_PROFILER_GET_IFACE (self)->stop (self); +} + +void +sp_profiler_add_pid (SpProfiler *self, + GPid pid) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (pid > -1); + + SP_PROFILER_GET_IFACE (self)->add_pid (self, pid); +} + +void +sp_profiler_remove_pid (SpProfiler *self, + GPid pid) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (pid > -1); + + SP_PROFILER_GET_IFACE (self)->remove_pid (self, pid); +} + +const GPid * +sp_profiler_get_pids (SpProfiler *self, + guint *n_pids) +{ + g_return_val_if_fail (SP_IS_PROFILER (self), NULL); + g_return_val_if_fail (n_pids != NULL, NULL); + + return SP_PROFILER_GET_IFACE (self)->get_pids (self, n_pids); +} + +void +sp_profiler_emit_failed (SpProfiler *self, + const GError *error) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + g_return_if_fail (error != NULL); + + g_signal_emit (self, signals [FAILED], 0, error); +} + +void +sp_profiler_emit_stopped (SpProfiler *self) +{ + g_return_if_fail (SP_IS_PROFILER (self)); + + g_signal_emit (self, signals [STOPPED], 0); } diff --git a/lib/sp-profiler.h b/lib/sp-profiler.h index 530b6b9a..133b8aa3 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 f864b70b..0ff7db7b 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 06ffbf82..59cf4313 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 f94ca414..38704e2d 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",