[gnome-builder/wip/gtk4-port: 1641/1774] libide/foundry: port plugins to IdeRunTool




commit 16971ae1840012c201ab457a97a212e07bd6d0ed
Author: Christian Hergert <chergert redhat com>
Date:   Fri Jun 24 03:35:41 2022 -0700

    libide/foundry: port plugins to IdeRunTool
    
    This is the first step in moving away from the IdeRunHandler, and another
    step towards removing IdeRunner. I notice some things still wrong with
    regular running, debugging and what-not seems to work.
    
    Sysprof is still unported, but this moves us in a direction that is much
    nicer for handling that too.

 src/libide/foundry/ide-run-manager-private.h       |  14 +-
 src/libide/foundry/ide-run-manager.c               | 273 +++++++----------
 src/libide/foundry/ide-run-manager.h               |  26 +-
 src/libide/gui/ide-run-button.c                    | 101 ++-----
 src/plugins/debuggerui/debuggerui-plugin.c         |   4 +
 src/plugins/debuggerui/gbp-debugger-tool.c         | 146 +++++++++
 src/plugins/debuggerui/gbp-debugger-tool.h         |  31 ++
 src/plugins/debuggerui/gtk/menus.ui                |   4 +-
 .../debuggerui/ide-debugger-workspace-addin.c      | 122 --------
 src/plugins/debuggerui/meson.build                 |   1 +
 src/plugins/sysprof/gbp-sysprof-tool.c             | 104 +++++++
 src/plugins/sysprof/gbp-sysprof-tool.h             |  31 ++
 src/plugins/sysprof/gbp-sysprof-workspace-addin.c  | 278 +----------------
 src/plugins/sysprof/meson.build                    |   3 +
 .../sysprof/org.gnome.builder.sysprof.gschema.xml  |  16 +
 src/plugins/sysprof/sysprof-plugin.c               |   4 +
 src/plugins/valgrind/gbp-valgrind-tool.c           | 204 +++++++++++++
 src/plugins/valgrind/gbp-valgrind-tool.h           |  31 ++
 .../valgrind/gbp-valgrind-workbench-addin.c        | 335 ++-------------------
 src/plugins/valgrind/meson.build                   |   3 +
 .../org.gnome.builder.valgrind.gschema.xml         |  28 ++
 src/plugins/valgrind/valgrind-plugin.c             |   4 +
 22 files changed, 781 insertions(+), 982 deletions(-)
---
diff --git a/src/libide/foundry/ide-run-manager-private.h b/src/libide/foundry/ide-run-manager-private.h
index 847a27a85..8b2e7c762 100644
--- a/src/libide/foundry/ide-run-manager-private.h
+++ b/src/libide/foundry/ide-run-manager-private.h
@@ -24,18 +24,6 @@
 
 G_BEGIN_DECLS
 
-typedef struct
-{
-  char           *id;
-  char           *title;
-  char           *icon_name;
-  int             priority;
-  IdeRunHandler   handler;
-  gpointer        handler_data;
-  GDestroyNotify  handler_data_destroy;
-} IdeRunHandlerInfo;
-
-const GList *_ide_run_manager_get_handlers (IdeRunManager *self);
-void         _ide_run_manager_drop_caches  (IdeRunManager *self);
+void _ide_run_manager_drop_caches (IdeRunManager *self);
 
 G_END_DECLS
diff --git a/src/libide/foundry/ide-run-manager.c b/src/libide/foundry/ide-run-manager.c
index 303c00695..06c78e2ea 100644
--- a/src/libide/foundry/ide-run-manager.c
+++ b/src/libide/foundry/ide-run-manager.c
@@ -41,11 +41,13 @@
 #include "ide-deploy-strategy.h"
 #include "ide-device-manager.h"
 #include "ide-foundry-compat.h"
+#include "ide-no-tool-private.h"
 #include "ide-run-command.h"
 #include "ide-run-command-provider.h"
 #include "ide-run-context.h"
 #include "ide-run-manager-private.h"
 #include "ide-run-manager.h"
+#include "ide-run-tool-private.h"
 #include "ide-runner.h"
 #include "ide-runtime.h"
 
@@ -56,9 +58,8 @@ struct _IdeRunManager
   GCancellable            *cancellable;
   IdeNotification         *notif;
   IdeExtensionSetAdapter  *run_command_providers;
-
-  const IdeRunHandlerInfo *handler;
-  GList                   *handlers;
+  IdeExtensionSetAdapter  *run_tools;
+  IdeRunTool              *run_tool;
 
   IdeSubprocess           *current_subprocess;
   IdeRunCommand           *current_run_command;
@@ -115,7 +116,8 @@ G_DEFINE_TYPE_EXTENDED (IdeRunManager, ide_run_manager, IDE_TYPE_OBJECT, G_TYPE_
 enum {
   PROP_0,
   PROP_BUSY,
-  PROP_HANDLER,
+  PROP_ICON_NAME,
+  PROP_RUN_TOOL,
   N_PROPS
 };
 
@@ -129,6 +131,57 @@ enum {
 static GParamSpec *properties [N_PROPS];
 static guint signals [N_SIGNALS];
 
+static IdeRunTool *
+ide_run_manager_get_run_tool (IdeRunManager *self)
+{
+  g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (IDE_IS_RUN_TOOL (self->run_tool));
+
+  return self->run_tool;
+}
+
+void
+ide_run_manager_set_run_tool_from_plugin_info (IdeRunManager  *self,
+                                               PeasPluginInfo *plugin_info)
+{
+  g_autoptr(IdeRunTool) no_tool = NULL;
+  PeasExtension *exten = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_RUN_MANAGER (self));
+
+  if (plugin_info != NULL)
+    exten = ide_extension_set_adapter_get_extension (self->run_tools, plugin_info);
+
+  if (exten == NULL)
+    {
+      if (IDE_IS_NO_TOOL (self->run_tool))
+        return;
+      no_tool = ide_no_tool_new ();
+      exten = (PeasExtension *)no_tool;
+    }
+
+  if (g_set_object (&self->run_tool, IDE_RUN_TOOL (exten)))
+    g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RUN_TOOL]);
+}
+
+static void
+ide_run_manager_set_run_tool_from_module_name (IdeRunManager *self,
+                                               const char    *name)
+{
+  PeasPluginInfo *plugin_info = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_RUN_MANAGER (self));
+
+  g_debug ("Looking for run-tool from module %s", name);
+
+  if (!ide_str_empty0 (name))
+    plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), name);
+
+  ide_run_manager_set_run_tool_from_plugin_info (self, plugin_info);
+}
+
 static void
 ide_run_manager_actions_high_contrast (IdeRunManager *self,
                                        GVariant      *param)
@@ -203,26 +256,6 @@ ide_run_manager_actions_default_run_command (IdeRunManager *self,
     }
 }
 
-static void
-ide_run_handler_info_free (gpointer data)
-{
-  IdeRunHandlerInfo *info = data;
-
-  g_clear_pointer (&info->id, g_free);
-  g_clear_pointer (&info->title, g_free);
-  g_clear_pointer (&info->icon_name, g_free);
-
-  if (info->handler_data_destroy)
-    {
-      GDestroyNotify notify = g_steal_pointer (&info->handler_data_destroy);
-      gpointer notify_data = g_steal_pointer (&info->handler_data);
-
-      notify (notify_data);
-    }
-
-  g_slice_free (IdeRunHandlerInfo, info);
-}
-
 static void
 ide_run_manager_update_action_enabled (IdeRunManager *self)
 {
@@ -285,18 +318,15 @@ ide_run_manager_dispose (GObject *object)
 {
   IdeRunManager *self = (IdeRunManager *)object;
 
-  self->handler = NULL;
-
   g_clear_pointer (&self->default_run_command, g_free);
 
   g_clear_object (&self->cancellable);
   g_clear_object (&self->current_run_command);
   g_clear_object (&self->current_subprocess);
+  g_clear_object (&self->run_tool);
 
   ide_clear_and_destroy_object (&self->run_command_providers);
-
-  g_list_free_full (self->handlers, ide_run_handler_info_free);
-  self->handlers = NULL;
+  ide_clear_and_destroy_object (&self->run_tools);
 
   G_OBJECT_CLASS (ide_run_manager_parent_class)->dispose (object);
 }
@@ -317,6 +347,17 @@ ide_run_manager_notify_can_build (IdeRunManager   *self,
   IDE_EXIT;
 }
 
+const char *
+ide_run_manager_get_icon_name (IdeRunManager *self)
+{
+  g_assert (IDE_IS_RUN_MANAGER (self));
+
+  if (self->run_tool == NULL)
+    return NULL;
+
+  return ide_run_tool_get_icon_name (self->run_tool);
+}
+
 static gboolean
 initable_init (GInitable     *initable,
                GCancellable  *cancellable,
@@ -347,6 +388,11 @@ initable_init (GInitable     *initable,
                                                                IDE_TYPE_RUN_COMMAND_PROVIDER,
                                                                NULL, NULL);
 
+  self->run_tools = ide_extension_set_adapter_new (IDE_OBJECT (self),
+                                                   peas_engine_get_default (),
+                                                   IDE_TYPE_RUN_TOOL,
+                                                   NULL, NULL);
+
   IDE_RETURN (TRUE);
 }
 
@@ -370,8 +416,12 @@ ide_run_manager_get_property (GObject    *object,
       g_value_set_boolean (value, ide_run_manager_get_busy (self));
       break;
 
-    case PROP_HANDLER:
-      g_value_set_string (value, ide_run_manager_get_handler (self));
+    case PROP_ICON_NAME:
+      g_value_set_string (value, ide_run_manager_get_icon_name (self));
+      break;
+
+    case PROP_RUN_TOOL:
+      g_value_set_object (value, ide_run_manager_get_run_tool (self));
       break;
 
     default:
@@ -388,17 +438,18 @@ ide_run_manager_class_init (IdeRunManagerClass *klass)
   object_class->get_property = ide_run_manager_get_property;
 
   properties [PROP_BUSY] =
-    g_param_spec_boolean ("busy",
-                          "Busy",
-                          "Busy",
+    g_param_spec_boolean ("busy", NULL, NULL,
                           FALSE,
                           (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
-  properties [PROP_HANDLER] =
-    g_param_spec_string ("handler",
-                         "Handler",
-                         "Handler",
-                         "run",
+  properties [PROP_ICON_NAME] =
+    g_param_spec_string ("icon-name", NULL, NULL,
+                         NULL,
+                         (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_RUN_TOOL] =
+    g_param_spec_object ("run-tool", NULL, NULL,
+                         IDE_TYPE_RUN_TOOL,
                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
@@ -745,6 +796,7 @@ ide_run_manager_run_subprocess_wait_check_cb (GObject      *object,
 
   IDE_ENTRY;
 
+  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_SUBPROCESS (subprocess));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
@@ -763,6 +815,8 @@ ide_run_manager_run_subprocess_wait_check_cb (GObject      *object,
   else
     ide_task_return_boolean (task, TRUE);
 
+  _ide_run_tool_emit_stopped (self->run_tool);
+
   g_signal_emit (self, signals[STOPPED], 0);
 
   IDE_EXIT;
@@ -782,17 +836,16 @@ ide_run_manager_prepare_run_context (IdeRunManager *self,
   g_assert (IDE_IS_RUN_CONTEXT (run_context));
   g_assert (IDE_IS_RUN_COMMAND (run_command));
   g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUN_TOOL (self->run_tool));
+
+  g_debug ("Preparing run context using run tool %s",
+           G_OBJECT_TYPE_NAME (self->run_tool));
 
-  /* The very first thing we need to do is allow the current run handler
+  /* The very first thing we need to do is allow the current run tool
    * to inject any command wrapper it needs. This might be something like
    * gdb, or valgrind, etc.
    */
-  if (self->handler != NULL && self->handler->handler != NULL)
-    self->handler->handler (self,
-                            pipeline,
-                            run_command,
-                            run_context,
-                            self->handler->handler_data);
+  ide_run_tool_prepare_to_run (self->run_tool, pipeline, run_command, run_context);
 
   /* Now push a new layer so that we can keep those values separate from
    * what is configured in the run command. We use an expansion layer so
@@ -937,6 +990,8 @@ ide_run_manager_run_deploy_cb (GObject      *object,
     ide_notification_attach (self->notif, IDE_OBJECT (self));
   }
 
+  _ide_run_tool_emit_started (self->run_tool, subprocess);
+
   g_signal_emit (self, signals[STARTED], 0);
 
   /* Wait for the application to finish running */
@@ -1182,9 +1237,9 @@ ide_run_manager_cancel (IdeRunManager *self)
       int exit_signal = ide_run_manager_get_exit_signal (self);
 
       if (!self->sent_signal)
-        ide_subprocess_send_signal (self->current_subprocess, exit_signal);
+        ide_run_tool_send_signal (self->run_tool, exit_signal);
       else
-        ide_subprocess_force_exit (self->current_subprocess);
+        ide_run_tool_force_exit (self->run_tool);
 
       self->sent_signal = TRUE;
     }
@@ -1197,103 +1252,6 @@ ide_run_manager_cancel (IdeRunManager *self)
   IDE_EXIT;
 }
 
-void
-ide_run_manager_set_handler (IdeRunManager *self,
-                             const gchar   *id)
-{
-  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
-
-  self->handler = NULL;
-
-  for (GList *iter = self->handlers; iter; iter = iter->next)
-    {
-      const IdeRunHandlerInfo *info = iter->data;
-
-      if (g_strcmp0 (info->id, id) == 0)
-        {
-          self->handler = info;
-          IDE_TRACE_MSG ("run handler set to %s", info->title);
-          g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HANDLER]);
-          break;
-        }
-    }
-}
-
-void
-ide_run_manager_add_handler (IdeRunManager  *self,
-                             const gchar    *id,
-                             const gchar    *title,
-                             const gchar    *icon_name,
-                             IdeRunHandler   run_handler,
-                             gpointer        user_data,
-                             GDestroyNotify  user_data_destroy)
-{
-  IdeRunHandlerInfo *info;
-
-  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
-  g_return_if_fail (id != NULL);
-  g_return_if_fail (title != NULL);
-
-  info = g_slice_new0 (IdeRunHandlerInfo);
-  info->id = g_strdup (id);
-  info->title = g_strdup (title);
-  info->icon_name = g_strdup (icon_name);
-  info->handler = run_handler;
-  info->handler_data = user_data;
-  info->handler_data_destroy = user_data_destroy;
-
-  self->handlers = g_list_append (self->handlers, info);
-
-  if (self->handler == NULL)
-    self->handler = info;
-}
-
-void
-ide_run_manager_remove_handler (IdeRunManager *self,
-                                const gchar   *id)
-{
-  g_return_if_fail (IDE_IS_RUN_MANAGER (self));
-  g_return_if_fail (id != NULL);
-
-  for (GList *iter = self->handlers; iter; iter = iter->next)
-    {
-      IdeRunHandlerInfo *info = iter->data;
-
-      if (g_strcmp0 (info->id, id) == 0)
-        {
-          self->handlers = g_list_delete_link (self->handlers, iter);
-
-          if (self->handler == info && self->handlers != NULL)
-            self->handler = self->handlers->data;
-          else
-            self->handler = NULL;
-
-          ide_run_handler_info_free (info);
-
-          break;
-        }
-    }
-}
-
-const GList *
-_ide_run_manager_get_handlers (IdeRunManager *self)
-{
-  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
-
-  return self->handlers;
-}
-
-const gchar *
-ide_run_manager_get_handler (IdeRunManager *self)
-{
-  g_return_val_if_fail (IDE_IS_RUN_MANAGER (self), NULL);
-
-  if (self->handler != NULL)
-    return self->handler->id;
-
-  return NULL;
-}
-
 static void
 ide_run_manager_run_action_cb (GObject      *object,
                                GAsyncResult *result,
@@ -1333,23 +1291,13 @@ static void
 ide_run_manager_actions_run_with_handler (IdeRunManager *self,
                                           GVariant      *param)
 {
-  const gchar *handler = NULL;
-  g_autoptr(GVariant) sunk = NULL;
-
   IDE_ENTRY;
 
+  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_RUN_MANAGER (self));
+  g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
 
-  if (param != NULL)
-  {
-    handler = g_variant_get_string (param, NULL);
-    if (g_variant_is_floating (param))
-      sunk = g_variant_ref_sink (param);
-  }
-
-  /* Use specified handler, if provided */
-  if (!ide_str_empty0 (handler))
-    ide_run_manager_set_handler (self, handler);
+  ide_run_manager_set_run_tool_from_module_name (self, g_variant_get_string (param, NULL));
 
   ide_run_manager_run_async (self,
                              NULL,
@@ -1378,6 +1326,7 @@ ide_run_manager_init (IdeRunManager *self)
   GtkTextDirection text_dir;
 
   self->cancellable = g_cancellable_new ();
+  self->run_tool = ide_no_tool_new ();
 
   /* Setup initial text direction state */
   text_dir = gtk_widget_get_default_direction ();
@@ -1387,14 +1336,6 @@ ide_run_manager_init (IdeRunManager *self)
                                       text_dir == GTK_TEXT_DIR_LTR ?
                                         g_variant_new_string ("ltr") :
                                         g_variant_new_string ("rtl"));
-
-  ide_run_manager_add_handler (self,
-                               "run",
-                               _("Run"),
-                               "builder-run-start-symbolic",
-                               NULL,
-                               NULL,
-                               NULL);
 }
 
 void
diff --git a/src/libide/foundry/ide-run-manager.h b/src/libide/foundry/ide-run-manager.h
index eeba81a0b..23f08cf72 100644
--- a/src/libide/foundry/ide-run-manager.h
+++ b/src/libide/foundry/ide-run-manager.h
@@ -25,6 +25,7 @@
 #endif
 
 #include <libide-core.h>
+#include <libide-plugins.h>
 
 #include "ide-foundry-types.h"
 
@@ -35,34 +36,17 @@ G_BEGIN_DECLS
 IDE_AVAILABLE_IN_ALL
 G_DECLARE_FINAL_TYPE (IdeRunManager, ide_run_manager, IDE, RUN_MANAGER, IdeObject)
 
-typedef void (*IdeRunHandler) (IdeRunManager *self,
-                               IdePipeline   *pipeline,
-                               IdeRunCommand *run_command,
-                               IdeRunContext *run_context,
-                               gpointer       user_data);
-
 IDE_AVAILABLE_IN_ALL
 IdeRunManager  *ide_run_manager_from_context                   (IdeContext           *context);
 IDE_AVAILABLE_IN_ALL
 void            ide_run_manager_cancel                         (IdeRunManager        *self);
 IDE_AVAILABLE_IN_ALL
-gboolean        ide_run_manager_get_busy                       (IdeRunManager        *self);
-IDE_AVAILABLE_IN_ALL
-const gchar    *ide_run_manager_get_handler                    (IdeRunManager        *self);
+const char     *ide_run_manager_get_icon_name                  (IdeRunManager        *self);
 IDE_AVAILABLE_IN_ALL
-void            ide_run_manager_set_handler                    (IdeRunManager        *self,
-                                                                const gchar          *id);
-IDE_AVAILABLE_IN_ALL
-void            ide_run_manager_add_handler                    (IdeRunManager        *self,
-                                                                const char           *id,
-                                                                const char           *title,
-                                                                const char           *icon_name,
-                                                                IdeRunHandler         run_handler,
-                                                                gpointer              user_data,
-                                                                GDestroyNotify        user_data_destroy);
+gboolean        ide_run_manager_get_busy                       (IdeRunManager        *self);
 IDE_AVAILABLE_IN_ALL
-void            ide_run_manager_remove_handler                 (IdeRunManager        *self,
-                                                                const gchar          *id);
+void            ide_run_manager_set_run_tool_from_plugin_info  (IdeRunManager        *self,
+                                                                PeasPluginInfo       *plugin_info);
 IDE_AVAILABLE_IN_ALL
 void            ide_run_manager_run_async                      (IdeRunManager        *self,
                                                                 GCancellable         *cancellable,
diff --git a/src/libide/gui/ide-run-button.c b/src/libide/gui/ide-run-button.c
index a942f34d2..f44f35f5e 100644
--- a/src/libide/gui/ide-run-button.c
+++ b/src/libide/gui/ide-run-button.c
@@ -38,44 +38,15 @@ struct _IdeRunButton
 {
   GtkWidget       parent_instance;
   AdwSplitButton *split_button;
-  char           *run_handler_icon_name;
   IdeJoinedMenu  *joined_menu;
 };
 
 G_DEFINE_FINAL_TYPE (IdeRunButton, ide_run_button, GTK_TYPE_WIDGET)
 
 static void
-ide_run_button_handler_set (IdeRunButton  *self,
-                            GParamSpec    *pspec,
-                            IdeRunManager *run_manager)
-{
-  const GList *list;
-  const GList *iter;
-  const gchar *handler;
-
-  g_assert (IDE_IS_RUN_BUTTON (self));
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-
-  handler = ide_run_manager_get_handler (run_manager);
-  list = _ide_run_manager_get_handlers (run_manager);
-
-  for (iter = list; iter; iter = iter->next)
-    {
-      const IdeRunHandlerInfo *info = iter->data;
-
-      if (g_strcmp0 (info->id, handler) == 0)
-        {
-          self->run_handler_icon_name = g_strdup (info->icon_name);
-          g_object_set (self->split_button, "icon-name", info->icon_name, NULL);
-          break;
-        }
-    }
-}
-
-static void
-on_run_busy_state_changed_cb (IdeRunButton  *self,
-                              GParamSpec    *pspec,
-                              IdeRunManager *run_manager)
+on_icon_state_changed_cb (IdeRunButton  *self,
+                          GParamSpec    *pspec,
+                          IdeRunManager *run_manager)
 {
   const char *icon_name;
   const char *action_name;
@@ -85,7 +56,7 @@ on_run_busy_state_changed_cb (IdeRunButton  *self,
 
   if (!ide_run_manager_get_busy (run_manager))
     {
-      icon_name = self->run_handler_icon_name;
+      icon_name = ide_run_manager_get_icon_name (run_manager);
       action_name = "run-manager.run";
     }
   else
@@ -94,8 +65,10 @@ on_run_busy_state_changed_cb (IdeRunButton  *self,
       action_name = "run-manager.stop";
     }
 
-  g_object_set (self->split_button, "icon-name", icon_name, NULL);
-  gtk_actionable_set_action_name (GTK_ACTIONABLE (self->split_button), action_name);
+  g_object_set (self->split_button,
+                "action-name", action_name,
+                "icon-name", icon_name,
+                NULL);
 }
 
 static void
@@ -114,27 +87,25 @@ ide_run_button_load (IdeRunButton *self,
   if (!ide_context_has_project (context))
     IDE_EXIT;
 
-  device_manager = ide_device_manager_from_context (context);
+  /* Setup button action/icon */
   run_manager = ide_run_manager_from_context (context);
-
   g_signal_connect_object (run_manager,
                            "notify::busy",
-                           G_CALLBACK (on_run_busy_state_changed_cb),
+                           G_CALLBACK (on_icon_state_changed_cb),
                            self,
                            G_CONNECT_SWAPPED);
-
   g_signal_connect_object (run_manager,
-                           "notify::handler",
-                           G_CALLBACK (ide_run_button_handler_set),
+                           "notify::icon-name",
+                           G_CALLBACK (on_icon_state_changed_cb),
                            self,
                            G_CONNECT_SWAPPED);
+  on_icon_state_changed_cb (self, NULL, run_manager);
 
   /* Add devices section */
+  device_manager = ide_device_manager_from_context (context);
   menu = _ide_device_manager_get_menu (device_manager);
   ide_joined_menu_prepend_menu (self->joined_menu, G_MENU_MODEL (menu));
 
-  ide_run_button_handler_set (self, NULL, run_manager);
-
   IDE_EXIT;
 }
 
@@ -160,9 +131,6 @@ ide_run_button_query_tooltip (IdeRunButton *self,
                               GtkButton    *button)
 {
   IdeRunManager *run_manager;
-  const GList *list;
-  const GList *iter;
-  const gchar *handler;
   IdeContext *context;
 
   g_assert (IDE_IS_RUN_BUTTON (self));
@@ -171,46 +139,13 @@ ide_run_button_query_tooltip (IdeRunButton *self,
 
   context = ide_widget_get_context (GTK_WIDGET (self));
   run_manager = ide_run_manager_from_context (context);
-  handler = ide_run_manager_get_handler (run_manager);
-  list = _ide_run_manager_get_handlers (run_manager);
 
   if (ide_run_manager_get_busy (run_manager))
-    {
-      gtk_tooltip_set_text (tooltip, _("Stop running"));
-      return TRUE;
-    }
-
-  for (iter = list; iter; iter = iter->next)
-    {
-      const IdeRunHandlerInfo *info = iter->data;
-
-      if (g_strcmp0 (info->id, handler) == 0)
-        {
-          gboolean enabled;
-
-          /* Figure out if the run action is enabled. If it
-           * is not, then we should inform the user that
-           * the project cannot be run yet because the
-           * build pipeline is not yet configured. */
-          g_action_group_query_action (G_ACTION_GROUP (run_manager),
-                                       "run",
-                                       &enabled,
-                                       NULL,
-                                       NULL,
-                                       NULL,
-                                       NULL);
-
-          if (!enabled)
-            {
-              gtk_tooltip_set_text (tooltip, _("Invalid project configuration"));
-              return TRUE;
-            }
-
-          gtk_tooltip_set_text (tooltip, info->title);
-        }
-    }
+    gtk_tooltip_set_text (tooltip, _("Stop running"));
+  else
+    gtk_tooltip_set_text (tooltip, _("Run project"));
 
-  return FALSE;
+  return TRUE;
 }
 
 static void
diff --git a/src/plugins/debuggerui/debuggerui-plugin.c b/src/plugins/debuggerui/debuggerui-plugin.c
index 6412c2a37..ddcc054d7 100644
--- a/src/plugins/debuggerui/debuggerui-plugin.c
+++ b/src/plugins/debuggerui/debuggerui-plugin.c
@@ -27,12 +27,16 @@
 #include <libide-debugger.h>
 #include <libide-gui.h>
 
+#include "gbp-debugger-tool.h"
 #include "ide-debugger-hover-provider.h"
 #include "ide-debugger-workspace-addin.h"
 
 void
 _gbp_debuggerui_register_types (PeasObjectModule *module)
 {
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_RUN_TOOL,
+                                              GBP_TYPE_DEBUGGER_TOOL);
   peas_object_module_register_extension_type (module,
                                               GTK_SOURCE_TYPE_HOVER_PROVIDER,
                                               IDE_TYPE_DEBUGGER_HOVER_PROVIDER);
diff --git a/src/plugins/debuggerui/gbp-debugger-tool.c b/src/plugins/debuggerui/gbp-debugger-tool.c
new file mode 100644
index 000000000..cce27695e
--- /dev/null
+++ b/src/plugins/debuggerui/gbp-debugger-tool.c
@@ -0,0 +1,146 @@
+/* gbp-debugger-tool.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-debugger-tool"
+
+#include "config.h"
+
+#include <libide-debugger.h>
+
+#include "ide-debug-manager-private.h"
+
+#include "gbp-debugger-tool.h"
+
+struct _GbpDebuggerTool
+{
+  IdeRunTool parent_instance;
+};
+
+G_DEFINE_FINAL_TYPE (GbpDebuggerTool, gbp_debugger_tool, IDE_TYPE_RUN_TOOL)
+
+static void
+gbp_debugger_tool_send_signal (IdeRunTool *run_tool,
+                               int         signum)
+{
+  GbpDebuggerTool *self = (GbpDebuggerTool *)run_tool;
+  IdeDebugManager *debug_manager;
+  IdeDebugger *debugger;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_DEBUGGER_TOOL (self));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  debug_manager = ide_debug_manager_from_context (context);
+  debugger = ide_debug_manager_get_debugger (debug_manager);
+
+  if (debugger != NULL)
+    ide_debugger_send_signal_async (debugger, signum, NULL, NULL, NULL);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_debugger_tool_prepare_to_run (IdeRunTool    *run_tool,
+                                  IdePipeline   *pipeline,
+                                  IdeRunCommand *run_command,
+                                  IdeRunContext *run_context)
+{
+  GbpDebuggerTool *self = (GbpDebuggerTool *)run_tool;
+  IdeDebugManager *debug_manager;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_DEBUGGER_TOOL (self));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUN_COMMAND (run_command));
+  g_assert (IDE_IS_RUN_CONTEXT (run_context));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  debug_manager = ide_debug_manager_from_context (context);
+
+  _ide_debug_manager_prepare (debug_manager, pipeline, run_command, run_context, NULL);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_debugger_tool_started (IdeRunTool    *run_tool,
+                           IdeSubprocess *subprocess)
+{
+  GbpDebuggerTool *self = (GbpDebuggerTool *)run_tool;
+  IdeDebugManager *debug_manager;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_DEBUGGER_TOOL (self));
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  debug_manager = ide_debug_manager_from_context (context);
+
+  _ide_debug_manager_started (debug_manager);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_debugger_tool_stopped (IdeRunTool *run_tool)
+{
+  GbpDebuggerTool *self = (GbpDebuggerTool *)run_tool;
+  IdeDebugManager *debug_manager;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_DEBUGGER_TOOL (self));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  debug_manager = ide_debug_manager_from_context (context);
+
+  _ide_debug_manager_stopped (debug_manager);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_debugger_tool_class_init (GbpDebuggerToolClass *klass)
+{
+  IdeRunToolClass *run_tool_class = IDE_RUN_TOOL_CLASS (klass);
+
+  run_tool_class->prepare_to_run = gbp_debugger_tool_prepare_to_run;
+  run_tool_class->send_signal = gbp_debugger_tool_send_signal;
+  run_tool_class->started = gbp_debugger_tool_started;
+  run_tool_class->stopped = gbp_debugger_tool_stopped;
+}
+
+static void
+gbp_debugger_tool_init (GbpDebuggerTool *self)
+{
+  ide_run_tool_set_icon_name (IDE_RUN_TOOL (self),
+                              "builder-debugger-symbolic");
+}
diff --git a/src/plugins/debuggerui/gbp-debugger-tool.h b/src/plugins/debuggerui/gbp-debugger-tool.h
new file mode 100644
index 000000000..fb876f3c2
--- /dev/null
+++ b/src/plugins/debuggerui/gbp-debugger-tool.h
@@ -0,0 +1,31 @@
+/* gbp-debugger-tool.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_DEBUGGER_TOOL (gbp_debugger_tool_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpDebuggerTool, gbp_debugger_tool, GBP, DEBUGGER_TOOL, IdeRunTool)
+
+G_END_DECLS
diff --git a/src/plugins/debuggerui/gtk/menus.ui b/src/plugins/debuggerui/gtk/menus.ui
index 27d3e8b54..655f7d73a 100644
--- a/src/plugins/debuggerui/gtk/menus.ui
+++ b/src/plugins/debuggerui/gtk/menus.ui
@@ -6,7 +6,7 @@
         <attribute name="id">debugger-run-handler</attribute>
         <attribute name="after">default-run-handler</attribute>
         <attribute name="action">run-manager.run-with-handler</attribute>
-        <attribute name="target">debugger</attribute>
+        <attribute name="target" type="s">'debuggerui'</attribute>
         <attribute name="label" translatable="yes">Run with Debugger</attribute>
         <attribute name="verb-icon-name">builder-debugger-symbolic</attribute>
         <attribute name="accel">&lt;control&gt;&lt;shift&gt;&lt;alt&gt;d</attribute>
@@ -42,7 +42,7 @@
             <attribute name="id">project-tree-menu-debug</attribute>
             <attribute name="label" translatable="yes">Run with _Debugger</attribute>
             <attribute name="action">buildui.run-with-handler</attribute>
-            <attribute name="target" type="s">'debugger'</attribute>
+            <attribute name="target" type="s">'debuggerui'</attribute>
           </item>
         </section>
       </submenu>
diff --git a/src/plugins/debuggerui/ide-debugger-workspace-addin.c 
b/src/plugins/debuggerui/ide-debugger-workspace-addin.c
index d3cadaa90..86c7f46bc 100644
--- a/src/plugins/debuggerui/ide-debugger-workspace-addin.c
+++ b/src/plugins/debuggerui/ide-debugger-workspace-addin.c
@@ -65,8 +65,6 @@ struct _IdeDebuggerWorkspaceAddin
   IdeWorkspace               *workspace;
   IdeWorkbench               *workbench;
 
-  IdeRunManager              *run_manager;
-
   IdeDebuggerDisassemblyView *disassembly_view;
   IdeDebuggerControls        *controls;
   IdeDebuggerBreakpointsView *breakpoints_view;
@@ -99,72 +97,6 @@ debugger_stopped (IdeDebuggerWorkspaceAddin *self,
   IDE_EXIT;
 }
 
-static void
-send_notification (IdeDebuggerWorkspaceAddin *self,
-                   const gchar            *title,
-                   const gchar            *body,
-                   const gchar            *icon_name,
-                   gboolean                urgent)
-{
-  g_autoptr(IdeNotification) notif = NULL;
-  g_autoptr(GIcon) icon = NULL;
-  IdeContext *context;
-
-  g_assert (IDE_IS_DEBUGGER_WORKSPACE_ADDIN (self));
-
-  context = ide_workbench_get_context (self->workbench);
-
-  if (icon_name)
-    icon = g_themed_icon_new (icon_name);
-
-  notif = g_object_new (IDE_TYPE_NOTIFICATION,
-                        "has-progress", FALSE,
-                        "icon", icon,
-                        "title", title,
-                        "body", body,
-                        "urgent", TRUE,
-                        NULL);
-  ide_notification_attach (notif, IDE_OBJECT (context));
-  ide_notification_withdraw_in_seconds (notif, 30);
-}
-
-static void
-debugger_run_handler (IdeRunManager *run_manager,
-                      IdePipeline   *pipeline,
-                      IdeRunCommand *run_command,
-                      IdeRunContext *run_context,
-                      gpointer       user_data)
-{
-  IdeDebuggerWorkspaceAddin *self = user_data;
-  g_autoptr(GError) error = NULL;
-  IdeDebugManager *debug_manager;
-  IdeContext *context;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-  g_assert (IDE_IS_PIPELINE (pipeline));
-  g_assert (IDE_IS_RUN_COMMAND (run_command));
-  g_assert (IDE_IS_RUN_CONTEXT (run_context));
-  g_assert (IDE_IS_DEBUGGER_WORKSPACE_ADDIN (self));
-
-  /*
-   * Get the currently configured debugger and attach it to our runner.
-   * It might need to prepend arguments like `gdb', `pdb', `mdb', etc.
-   */
-  context = ide_object_get_context (IDE_OBJECT (run_manager));
-  debug_manager = ide_debug_manager_from_context (context);
-
-  if (!_ide_debug_manager_prepare (debug_manager, pipeline, run_command, run_context, &error))
-    send_notification (self,
-                       _("Failed to start the debugger"),
-                       error->message,
-                       "computer-fail-symbolic",
-                       TRUE);
-
-  IDE_EXIT;
-}
-
 static void
 debug_manager_notify_debugger (IdeDebuggerWorkspaceAddin *self,
                                GParamSpec                *pspec,
@@ -339,41 +271,12 @@ ide_debugger_workspace_addin_add_ui (IdeDebuggerWorkspaceAddin *self)
   ide_workspace_add_pane (self->workspace, IDE_PANE (self->panel), position);
 }
 
-static void
-ide_debugger_workspace_addin_started_cb (IdeDebugManager *debug_manager,
-                                         IdeRunManager   *run_manager)
-{
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_DEBUG_MANAGER (debug_manager));
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-
-  _ide_debug_manager_started (debug_manager);
-
-  IDE_EXIT;
-}
-
-static void
-ide_debugger_workspace_addin_stopped_cb (IdeDebugManager *debug_manager,
-                                         IdeRunManager   *run_manager)
-{
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_DEBUG_MANAGER (debug_manager));
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-
-  _ide_debug_manager_stopped (debug_manager);
-
-  IDE_EXIT;
-}
-
 static void
 ide_debugger_workspace_addin_load (IdeWorkspaceAddin *addin,
                                    IdeWorkspace      *workspace)
 {
   IdeDebuggerWorkspaceAddin *self = (IdeDebuggerWorkspaceAddin *)addin;
   IdeDebugManager *debug_manager;
-  IdeRunManager *run_manager;
   IdeContext *context;
 
   IDE_ENTRY;
@@ -388,33 +291,10 @@ ide_debugger_workspace_addin_load (IdeWorkspaceAddin *addin,
     return;
 
   context = ide_widget_get_context (GTK_WIDGET (workspace));
-  run_manager = ide_run_manager_from_context (context);
   debug_manager = ide_debug_manager_from_context (context);
 
-  self->run_manager = g_object_ref (run_manager);
-
   ide_debugger_workspace_addin_add_ui (self);
 
-  ide_run_manager_add_handler (run_manager,
-                               "debugger",
-                               _("Run with Debugger"),
-                               "builder-debugger-symbolic",
-                               debugger_run_handler,
-                               g_object_ref (self),
-                               g_object_unref);
-
-  g_signal_connect_object (run_manager,
-                           "started",
-                           G_CALLBACK (ide_debugger_workspace_addin_started_cb),
-                           debug_manager,
-                           G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (run_manager,
-                           "stopped",
-                           G_CALLBACK (ide_debugger_workspace_addin_stopped_cb),
-                           debug_manager,
-                           G_CONNECT_SWAPPED);
-
   self->debugger_signals = ide_signal_group_new (IDE_TYPE_DEBUGGER);
 
   ide_signal_group_connect_swapped (self->debugger_signals,
@@ -459,13 +339,11 @@ ide_debugger_workspace_addin_unload (IdeWorkspaceAddin *addin,
     return;
 
   gtk_widget_insert_action_group (GTK_WIDGET (self->workspace), "debugger", NULL);
-  ide_run_manager_remove_handler (self->run_manager, "debugger");
 
   self->controls = NULL;
 
   g_clear_object (&self->debugger_signals);
   g_clear_object (&self->debug_manager_signals);
-  g_clear_object (&self->run_manager);
 
   ide_clear_pane ((IdePane **)&self->panel);
   ide_clear_page ((IdePage **)&self->disassembly_view);
diff --git a/src/plugins/debuggerui/meson.build b/src/plugins/debuggerui/meson.build
index 1a7aa48fe..eadaf71ef 100644
--- a/src/plugins/debuggerui/meson.build
+++ b/src/plugins/debuggerui/meson.build
@@ -1,5 +1,6 @@
 plugins_sources += files([
   'debuggerui-plugin.c',
+  'gbp-debugger-tool.c',
   'ide-debugger-breakpoints-view.c',
   'ide-debugger-controls.c',
   'ide-debugger-disassembly-view.c',
diff --git a/src/plugins/sysprof/gbp-sysprof-tool.c b/src/plugins/sysprof/gbp-sysprof-tool.c
new file mode 100644
index 000000000..8f469aaf3
--- /dev/null
+++ b/src/plugins/sysprof/gbp-sysprof-tool.c
@@ -0,0 +1,104 @@
+/* gbp-sysprof-tool.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-sysprof-tool"
+
+#include "config.h"
+
+#include "gbp-sysprof-tool.h"
+
+struct _GbpSysprofTool
+{
+  IdeRunTool parent_instance;
+};
+
+G_DEFINE_FINAL_TYPE (GbpSysprofTool, gbp_sysprof_tool, IDE_TYPE_RUN_TOOL)
+
+static void
+gbp_sysprof_tool_prepare_to_run (IdeRunTool    *run_tool,
+                                 IdePipeline   *pipeline,
+                                 IdeRunCommand *run_command,
+                                 IdeRunContext *run_context)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_SYSPROF_TOOL (run_tool));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUN_COMMAND (run_command));
+  g_assert (IDE_IS_RUN_CONTEXT (run_context));
+
+  g_printerr ("TODO: Port sysprof tool\n");
+
+  IDE_EXIT;
+}
+
+static void
+gbp_sysprof_tool_send_signal (IdeRunTool *run_tool,
+                              int         signum)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_SYSPROF_TOOL (run_tool));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_sysprof_tool_started (IdeRunTool    *run_tool,
+                          IdeSubprocess *subprocess)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_SYSPROF_TOOL (run_tool));
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_sysprof_tool_stopped (IdeRunTool *run_tool)
+{
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_SYSPROF_TOOL (run_tool));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_sysprof_tool_class_init (GbpSysprofToolClass *klass)
+{
+  IdeRunToolClass *run_tool_class = IDE_RUN_TOOL_CLASS (klass);
+
+  run_tool_class->prepare_to_run = gbp_sysprof_tool_prepare_to_run;
+  run_tool_class->send_signal = gbp_sysprof_tool_send_signal;
+  run_tool_class->started = gbp_sysprof_tool_started;
+  run_tool_class->stopped = gbp_sysprof_tool_stopped;
+}
+
+static void
+gbp_sysprof_tool_init (GbpSysprofTool *self)
+{
+  ide_run_tool_set_icon_name (IDE_RUN_TOOL (self), "builder-profiler-symbolic");
+}
diff --git a/src/plugins/sysprof/gbp-sysprof-tool.h b/src/plugins/sysprof/gbp-sysprof-tool.h
new file mode 100644
index 000000000..1ca0b4afb
--- /dev/null
+++ b/src/plugins/sysprof/gbp-sysprof-tool.h
@@ -0,0 +1,31 @@
+/* gbp-sysprof-tool.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_SYSPROF_TOOL (gbp_sysprof_tool_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpSysprofTool, gbp_sysprof_tool, GBP, SYSPROF_TOOL, IdeRunTool)
+
+G_END_DECLS
diff --git a/src/plugins/sysprof/gbp-sysprof-workspace-addin.c 
b/src/plugins/sysprof/gbp-sysprof-workspace-addin.c
index 295d28611..5e88639ed 100644
--- a/src/plugins/sysprof/gbp-sysprof-workspace-addin.c
+++ b/src/plugins/sysprof/gbp-sysprof-workspace-addin.c
@@ -39,248 +39,6 @@ struct _GbpSysprofWorkspaceAddin
   IdeRunManager      *run_manager;
 };
 
-static void
-set_state (GSimpleAction *action,
-           GVariant      *param,
-           gpointer       user_data)
-{
-  g_simple_action_set_state (action, param);
-}
-
-static gboolean
-get_state (GbpSysprofWorkspaceAddin *self,
-           const char               *action_name)
-{
-  g_autoptr(GVariant) state = NULL;
-  GAction *action;
-
-  g_assert (GBP_IS_SYSPROF_WORKSPACE_ADDIN (self));
-  g_assert (action_name != NULL);
-
-  if (!(action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), action_name)))
-    g_return_val_if_reached (FALSE);
-
-  if (!(state = g_action_get_state (action)))
-    g_return_val_if_reached (FALSE);
-
-  if (!g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
-    g_return_val_if_reached (FALSE);
-
-  return g_variant_get_boolean (state);
-}
-
-static void
-profiler_child_spawned (IdeRunner       *runner,
-                        const gchar     *identifier,
-                        SysprofProfiler *profiler)
-{
-#ifdef G_OS_UNIX
-  GPid pid = 0;
-
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-  g_assert (identifier != NULL);
-  g_assert (IDE_IS_RUNNER (runner));
-
-  pid = g_ascii_strtoll (identifier, NULL, 10);
-
-  if (pid == 0)
-    {
-      g_warning ("Failed to parse integer value from %s", identifier);
-      return;
-    }
-
-  IDE_TRACE_MSG ("Adding pid %s to profiler", identifier);
-
-  sysprof_profiler_add_pid (profiler, pid);
-  sysprof_profiler_start (profiler);
-#endif
-}
-
-static void
-runner_exited_cb (IdeRunner       *runner,
-                  SysprofProfiler *profiler)
-{
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (IDE_IS_RUNNER (runner));
-  g_assert (SYSPROF_IS_PROFILER (profiler));
-
-  if (sysprof_profiler_get_is_running (profiler))
-    sysprof_profiler_stop (profiler);
-}
-
-static void
-foreach_fd (gint     dest_fd,
-            gint     fd,
-            gpointer user_data)
-{
-  IdeRunner *runner = user_data;
-
-  g_assert (IDE_IS_RUNNER (runner));
-  g_assert (dest_fd >= 0);
-  g_assert (fd >= 0);
-
-  ide_runner_take_fd (runner, dup (fd), dest_fd);
-}
-
-static void
-profiler_run_handler (IdeRunManager *run_manager,
-                      IdePipeline   *pipeline,
-                      IdeRunCommand *run_command,
-                      IdeRunContext *run_context,
-                      gpointer       user_data)
-{
-  g_printerr ("TODO: sysprof not ported to run context yet!\n");
-
-#if 0
-  GbpSysprofWorkspaceAddin *self = user_data;
-  g_autoptr(SysprofProfiler) profiler = NULL;
-  g_autoptr(SysprofSpawnable) spawnable = NULL;
-  g_autoptr(IdePanelPosition) position = NULL;
-  g_autoptr(GPtrArray) sources = NULL;
-  g_auto(GStrv) argv = NULL;
-  const gchar * const *env;
-  GbpSysprofPage *page;
-  IdeEnvironment *ienv;
-
-  g_assert (IDE_IS_MAIN_THREAD ());
-  g_assert (GBP_IS_SYSPROF_WORKSPACE_ADDIN (self));
-  g_assert (IDE_IS_RUN_CONTEXT (run_context));
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-
-  sources = g_ptr_array_new_with_free_func (g_object_unref);
-
-  profiler = sysprof_local_profiler_new ();
-
-  /*
-   * Currently we require whole-system because otherwise we can get a situation
-   * where we only watch the spawning process (say jhbuild, flatpak, etc).
-   * Longer term we either need a way to follow-children and/or limit to a
-   * cgroup/process-group.
-   */
-  sysprof_profiler_set_whole_system (profiler, TRUE);
-
-#ifdef __linux__
-  {
-    g_ptr_array_add (sources, sysprof_proc_source_new ());
-
-    if (get_state (self, "cpu-aid"))
-      g_ptr_array_add (sources, sysprof_hostinfo_source_new ());
-
-    if (get_state (self, "perf-aid"))
-      g_ptr_array_add (sources, sysprof_perf_source_new ());
-
-    if (get_state (self, "memory-aid"))
-      g_ptr_array_add (sources, sysprof_memory_source_new ());
-
-    if (get_state (self, "energy-aid"))
-      g_ptr_array_add (sources, sysprof_proxy_source_new (G_BUS_TYPE_SYSTEM,
-                                                          "org.gnome.Sysprof3",
-                                                          "/org/gnome/Sysprof3/RAPL"));
-
-    if (get_state (self, "battery-aid"))
-      g_ptr_array_add (sources, sysprof_battery_source_new ());
-
-    if (get_state (self, "netstat-aid"))
-      g_ptr_array_add (sources, sysprof_netdev_source_new ());
-
-    if (get_state (self, "diskstat-aid"))
-      g_ptr_array_add (sources, sysprof_diskstat_source_new ());
-
-    if (!get_state (self, "allow-throttle"))
-      {
-        SysprofSource *governor = sysprof_governor_source_new ();
-        sysprof_governor_source_set_disable_governor (SYSPROF_GOVERNOR_SOURCE (governor), TRUE);
-        g_ptr_array_add (sources, governor);
-      }
-  }
-#endif
-
-  if (get_state (self, "memprof-aid"))
-    g_ptr_array_add (sources, sysprof_memprof_source_new ());
-
-  g_ptr_array_add (sources, sysprof_gjs_source_new ());
-  g_ptr_array_add (sources, sysprof_symbols_source_new ());
-
-  /* Allow the app to submit us data if it supports "SYSPROF_TRACE_FD" */
-  if (get_state (self, "allow-tracefd"))
-    {
-      SysprofSource *app_source = sysprof_tracefd_source_new ();
-      sysprof_tracefd_source_set_envvar (SYSPROF_TRACEFD_SOURCE (app_source), "SYSPROF_TRACE_FD");
-      g_ptr_array_add (sources, app_source);
-    }
-
-  /*
-   * TODO:
-   *
-   * We need to synchronize the inferior with the parent here. Ideally, we would
-   * prepend the application launch (to some degree) with the application we want
-   * to execute. In this case, we might want to add a "gnome-builder-sysprof"
-   * helper that will synchronize with the parent, and then block until we start
-   * the process (with the appropriate pid) before exec() otherwise we could
-   * miss the exit of the app and race to add the pid to the profiler.
-   */
-
-  g_signal_connect_object (runner,
-                           "spawned",
-                           G_CALLBACK (profiler_child_spawned),
-                           profiler,
-                           0);
-
-  g_signal_connect_object (runner,
-                           "exited",
-                           G_CALLBACK (runner_exited_cb),
-                           profiler,
-                           0);
-
-  /*
-   * We need to allow the sources to modify the execution environment, so copy
-   * the environment into the spawnable, modify it, and the propagate back.
-   */
-  argv = ide_runner_get_argv (runner);
-  ienv = ide_runner_get_environment (runner);
-
-  spawnable = sysprof_spawnable_new ();
-  sysprof_spawnable_append_args (spawnable, (const gchar * const *)argv);
-  sysprof_spawnable_set_starting_fd (spawnable, ide_runner_get_max_fd (runner) + 1);
-
-  for (guint i = 0; i < sources->len; i++)
-    {
-      SysprofSource *source = g_ptr_array_index (sources, i);
-
-      if (source != NULL)
-        {
-          sysprof_profiler_add_source (profiler, source);
-          sysprof_source_modify_spawn (source, spawnable);
-        }
-    }
-
-  /* TODO: Propagate argv back to runner.
-   *
-   * Currently this is a non-issue because none of our sources modify argv.
-   * So doing it now is just brittle for no benefit.
-   */
-
-  if ((env = sysprof_spawnable_get_environ (spawnable)))
-    {
-      for (guint i = 0; env[i] != NULL; i++)
-        {
-          g_autofree gchar *key = NULL;
-          g_autofree gchar *value = NULL;
-
-          if (ide_environ_parse (env[i], &key, &value))
-            ide_environment_setenv (ienv, key, value);
-        }
-    }
-
-  sysprof_spawnable_foreach_fd (spawnable, foreach_fd, runner);
-
-  page = gbp_sysprof_page_new_for_profiler (profiler);
-  position = ide_panel_position_new ();
-  ide_workspace_add_page (self->workspace, IDE_PAGE (page), position);
-#endif
-}
-
 static void
 gbp_sysprof_workspace_addin_open (GbpSysprofWorkspaceAddin *self,
                                   GFile                    *file)
@@ -383,12 +141,14 @@ run_cb (GSimpleAction *action,
         gpointer       user_data)
 {
   GbpSysprofWorkspaceAddin *self = user_data;
+  PeasPluginInfo *plugin_info;
 
   g_assert (GBP_IS_SYSPROF_WORKSPACE_ADDIN (self));
   g_assert (IDE_IS_WORKSPACE (self->workspace));
   g_assert (IDE_IS_RUN_MANAGER (self->run_manager));
 
-  ide_run_manager_set_handler (self->run_manager, "sysprof");
+  plugin_info = peas_engine_get_plugin_info (peas_engine_get_default (), "sysprof");
+  ide_run_manager_set_run_tool_from_plugin_info (self->run_manager, plugin_info);
   ide_run_manager_run_async (self->run_manager, NULL, NULL, NULL);
 }
 
@@ -422,13 +182,6 @@ gbp_sysprof_workspace_addin_check_supported_cb (GObject      *object,
   gtk_widget_insert_action_group (GTK_WIDGET (self->workspace),
                                   "sysprof",
                                   G_ACTION_GROUP (self->actions));
-  ide_run_manager_add_handler (self->run_manager,
-                               "sysprof",
-                               _("Run with Profiler"),
-                               "builder-profiler-symbolic",
-                               profiler_run_handler,
-                               self,
-                               NULL);
 
   IDE_EXIT;
 }
@@ -436,17 +189,6 @@ gbp_sysprof_workspace_addin_check_supported_cb (GObject      *object,
 static const GActionEntry entries[] = {
   { "open-capture", open_capture_action },
   { "run", run_cb },
-  { "cpu-aid", NULL, NULL, "true", set_state },
-  { "perf-aid", NULL, NULL, "true", set_state },
-  { "memory-aid", NULL, NULL, "true", set_state },
-  { "memprof-aid", NULL, NULL, "false", set_state },
-  { "diskstat-aid", NULL, NULL, "true", set_state },
-  { "netstat-aid", NULL, NULL, "true", set_state },
-  { "energy-aid", NULL, NULL, "false", set_state },
-  { "battery-aid", NULL, NULL, "false", set_state },
-  { "compositor-aid", NULL, NULL, "false", set_state },
-  { "allow-throttle", NULL, NULL, "true", set_state },
-  { "allow-tracefd", NULL, NULL, "true", set_state },
 };
 
 static void
@@ -454,6 +196,9 @@ gbp_sysprof_workspace_addin_load (IdeWorkspaceAddin *addin,
                                   IdeWorkspace      *workspace)
 {
   GbpSysprofWorkspaceAddin *self = (GbpSysprofWorkspaceAddin *)addin;
+  g_autoptr(GSettingsSchema) schema = NULL;
+  g_autoptr(GSettings) settings = NULL;
+  g_auto(GStrv) keys = NULL;
   IdeRunManager *run_manager;
   IdeContext *context;
 
@@ -476,6 +221,16 @@ gbp_sysprof_workspace_addin_load (IdeWorkspaceAddin *addin,
                                    G_N_ELEMENTS (entries),
                                    self);
 
+  settings = g_settings_new ("org.gnome.builder.sysprof");
+  g_object_get (settings, "settings-schema", &schema, NULL);
+  keys = g_settings_schema_list_keys (schema);
+
+  for (guint i = 0; keys[i]; i++)
+    {
+      g_autoptr(GAction) action = g_settings_create_action (settings, keys[i]);
+      g_action_map_add_action (G_ACTION_MAP (self->actions), action);
+    }
+
   g_object_bind_property (self->run_manager,
                           "busy",
                           g_action_map_lookup_action (G_ACTION_MAP (self->actions), "run"),
@@ -501,7 +256,6 @@ gbp_sysprof_workspace_addin_unload (IdeWorkspaceAddin *addin,
   g_assert (IDE_IS_WORKSPACE (workspace));
 
   gtk_widget_insert_action_group (GTK_WIDGET (workspace), "sysprof", NULL);
-  ide_run_manager_remove_handler (self->run_manager, "sysprof");
 
   g_clear_object (&self->actions);
   g_clear_object (&self->run_manager);
diff --git a/src/plugins/sysprof/meson.build b/src/plugins/sysprof/meson.build
index 6b240b0a5..b19174f80 100644
--- a/src/plugins/sysprof/meson.build
+++ b/src/plugins/sysprof/meson.build
@@ -8,6 +8,7 @@ plugins_deps += [
 plugins_sources += files([
   'sysprof-plugin.c',
   'gbp-sysprof-page.c',
+  'gbp-sysprof-tool.c',
   'gbp-sysprof-workbench-addin.c',
   'gbp-sysprof-workspace-addin.c',
 ])
@@ -20,4 +21,6 @@ plugin_sysprof_resources = gnome.compile_resources(
 
 plugins_sources += plugin_sysprof_resources
 
+install_data(['org.gnome.builder.sysprof.gschema.xml'], install_dir: schema_dir)
+
 endif
diff --git a/src/plugins/sysprof/org.gnome.builder.sysprof.gschema.xml 
b/src/plugins/sysprof/org.gnome.builder.sysprof.gschema.xml
new file mode 100644
index 000000000..b88e84c6e
--- /dev/null
+++ b/src/plugins/sysprof/org.gnome.builder.sysprof.gschema.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema id="org.gnome.builder.sysprof" path="/org/gnome/builder/sysprof/" gettext-domain="gnome-builder">
+    <key type="b" name="cpu-aid"><default>true</default></key>
+    <key type="b" name="perf-aid"><default>true</default></key>
+    <key type="b" name="memory-aid"><default>true</default></key>
+    <key type="b" name="memprof-aid"><default>false</default></key>
+    <key type="b" name="diskstat-aid"><default>true</default></key>
+    <key type="b" name="netstat-aid"><default>true</default></key>
+    <key type="b" name="energy-aid"><default>false</default></key>
+    <key type="b" name="battery-aid"><default>false</default></key>
+    <key type="b" name="compositor-aid"><default>false</default></key>
+    <key type="b" name="allow-throttle"><default>true</default></key>
+    <key type="b" name="allow-tracefd"><default>true</default></key>
+  </schema>
+</schemalist>
diff --git a/src/plugins/sysprof/sysprof-plugin.c b/src/plugins/sysprof/sysprof-plugin.c
index f2e721a6f..6d6c97db3 100644
--- a/src/plugins/sysprof/sysprof-plugin.c
+++ b/src/plugins/sysprof/sysprof-plugin.c
@@ -27,6 +27,7 @@
 
 #include <libide-gui.h>
 
+#include "gbp-sysprof-tool.h"
 #include "gbp-sysprof-workbench-addin.h"
 #include "gbp-sysprof-workspace-addin.h"
 
@@ -35,6 +36,9 @@ _gbp_sysprof_register_types (PeasObjectModule *module)
 {
   sysprof_clock_init ();
 
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_RUN_TOOL,
+                                              GBP_TYPE_SYSPROF_TOOL);
   peas_object_module_register_extension_type (module,
                                               IDE_TYPE_WORKBENCH_ADDIN,
                                               GBP_TYPE_SYSPROF_WORKBENCH_ADDIN);
diff --git a/src/plugins/valgrind/gbp-valgrind-tool.c b/src/plugins/valgrind/gbp-valgrind-tool.c
new file mode 100644
index 000000000..62c48ae97
--- /dev/null
+++ b/src/plugins/valgrind/gbp-valgrind-tool.c
@@ -0,0 +1,204 @@
+/* gbp-valgrind-tool.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-valgrind-tool"
+
+#include "config.h"
+
+#include <libide-gui.h>
+
+#include "gbp-valgrind-tool.h"
+
+struct _GbpValgrindTool
+{
+  IdeRunTool  parent_instance;
+  char       *log_name;
+};
+
+G_DEFINE_FINAL_TYPE (GbpValgrindTool, gbp_valgrind_tool, IDE_TYPE_RUN_TOOL)
+
+static gboolean
+gbp_valgrind_tool_handler_cb (IdeRunContext       *run_context,
+                              const char * const  *argv,
+                              const char * const  *env,
+                              const char          *cwd,
+                              IdeUnixFDMap        *unix_fd_map,
+                              gpointer             user_data,
+                              GError             **error)
+{
+  GbpValgrindTool *self = user_data;
+  g_autoptr(GSettings) settings = NULL;
+  g_autoptr(GString) leak_kinds = NULL;
+  g_autofree char *name = NULL;
+  g_autofree char *leak_check = NULL;
+  gboolean track_origins;
+  int source_fd;
+  int dest_fd;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_RUN_CONTEXT (run_context));
+  g_assert (IDE_IS_UNIX_FD_MAP (unix_fd_map));
+  g_assert (argv != NULL);
+  g_assert (env != NULL);
+  g_assert (GBP_IS_VALGRIND_TOOL (self));
+
+  settings = g_settings_new ("org.gnome.builder.valgrind");
+
+  if (cwd != NULL)
+    ide_run_context_set_cwd (run_context, cwd);
+
+  dest_fd = ide_unix_fd_map_get_max_dest_fd (unix_fd_map) + 1;
+  if (!ide_run_context_merge_unix_fd_map (run_context, unix_fd_map, error))
+    IDE_RETURN (FALSE);
+
+  /* Create a temp file to write to and an FD to access it */
+  if (-1 == (source_fd = g_file_open_tmp ("gnome-builder-valgrind-XXXXXX.txt", &name, error)))
+    IDE_RETURN (FALSE);
+
+  /* Set our FD for valgrind to log to */
+  ide_run_context_take_fd (run_context, source_fd, dest_fd);
+
+  /* Save the filename so we can open it after exiting */
+  ide_set_string (&self->log_name, name);
+  g_debug ("Using %s for valgrind log", name);
+
+  track_origins = g_settings_get_boolean (settings, "track-origins");
+  leak_check = g_settings_get_string (settings, "leak-check");
+
+  leak_kinds = g_string_new (NULL);
+  if (g_settings_get_boolean (settings, "leak-kind-definite"))
+    g_string_append (leak_kinds, "definite,");
+  if (g_settings_get_boolean (settings, "leak-kind-possible"))
+    g_string_append (leak_kinds, "possible,");
+  if (g_settings_get_boolean (settings, "leak-kind-indirect"))
+    g_string_append (leak_kinds, "indirect,");
+  if (g_settings_get_boolean (settings, "leak-kind-reachable"))
+    g_string_append (leak_kinds, "reachable,");
+
+  ide_run_context_append_argv (run_context, "valgrind");
+  ide_run_context_append_formatted (run_context, "--log-fd=%d", dest_fd);
+  ide_run_context_append_formatted (run_context, "--leak-check=%s", leak_check);
+  ide_run_context_append_formatted (run_context, "--track-origins=%s", track_origins ? "yes" : "no");
+
+  if (leak_kinds->len > 0)
+    {
+      g_string_truncate (leak_kinds, leak_kinds->len-1);
+      ide_run_context_append_formatted (run_context, "--show-leak-kinds=%s", leak_kinds->str);
+    }
+
+  if (env[0] != NULL)
+    {
+      /* If we have to exec "env" to pass environment variables, then we
+       * must follow children to get to our target executable.
+       */
+      ide_run_context_append_argv (run_context, "--trace-children=yes");
+      ide_run_context_append_argv (run_context, "env");
+      ide_run_context_append_args (run_context, env);
+    }
+
+  ide_run_context_append_args (run_context, argv);
+
+  IDE_RETURN (TRUE);
+}
+
+static void
+gbp_valgrind_tool_prepare_to_run (IdeRunTool    *run_tool,
+                                  IdePipeline   *pipeline,
+                                  IdeRunCommand *run_command,
+                                  IdeRunContext *run_context)
+{
+  GbpValgrindTool *self = (GbpValgrindTool *)run_tool;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_VALGRIND_TOOL (self));
+  g_assert (IDE_IS_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUN_COMMAND (run_command));
+  g_assert (IDE_IS_RUN_CONTEXT (run_context));
+
+  ide_run_context_push (run_context,
+                        gbp_valgrind_tool_handler_cb,
+                        g_object_ref (self),
+                        g_object_unref);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_valgrind_tool_stopped (IdeRunTool *run_tool)
+{
+  GbpValgrindTool *self = (GbpValgrindTool *)run_tool;
+  g_autoptr(GFile) file = NULL;
+  IdeWorkbench *workbench;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_VALGRIND_TOOL (self));
+
+  if (self->log_name == NULL)
+    IDE_EXIT;
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  workbench = ide_workbench_from_context (context);
+  file = g_file_new_for_path (self->log_name);
+
+  ide_workbench_open_async (workbench,
+                            file,
+                            "editorui",
+                            IDE_BUFFER_OPEN_FLAGS_NONE,
+                            NULL, NULL, NULL, NULL);
+
+  g_clear_pointer (&self->log_name, g_free);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_valgrind_tool_finalize (GObject *object)
+{
+  GbpValgrindTool *self = (GbpValgrindTool *)object;
+
+  g_clear_pointer (&self->log_name, g_free);
+
+  G_OBJECT_CLASS (gbp_valgrind_tool_parent_class)->finalize (object);
+}
+
+static void
+gbp_valgrind_tool_class_init (GbpValgrindToolClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeRunToolClass *run_tool_class = IDE_RUN_TOOL_CLASS (klass);
+
+  object_class->finalize = gbp_valgrind_tool_finalize;
+
+  run_tool_class->prepare_to_run = gbp_valgrind_tool_prepare_to_run;
+  run_tool_class->stopped = gbp_valgrind_tool_stopped;
+}
+
+static void
+gbp_valgrind_tool_init (GbpValgrindTool *self)
+{
+  ide_run_tool_set_icon_name (IDE_RUN_TOOL (self), "system-run-symbolic");
+}
diff --git a/src/plugins/valgrind/gbp-valgrind-tool.h b/src/plugins/valgrind/gbp-valgrind-tool.h
new file mode 100644
index 000000000..be8f5abaf
--- /dev/null
+++ b/src/plugins/valgrind/gbp-valgrind-tool.h
@@ -0,0 +1,31 @@
+/* gbp-valgrind-tool.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_VALGRIND_TOOL (gbp_valgrind_tool_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpValgrindTool, gbp_valgrind_tool, GBP, VALGRIND_TOOL, IdeRunTool)
+
+G_END_DECLS
diff --git a/src/plugins/valgrind/gbp-valgrind-workbench-addin.c 
b/src/plugins/valgrind/gbp-valgrind-workbench-addin.c
index a0ea1b367..173dab00a 100644
--- a/src/plugins/valgrind/gbp-valgrind-workbench-addin.c
+++ b/src/plugins/valgrind/gbp-valgrind-workbench-addin.c
@@ -34,289 +34,8 @@
 
 struct _GbpValgrindWorkbenchAddin
 {
-  GObject             parent_instance;
-
-  IdeWorkbench       *workbench;
-  IdeRunManager      *run_manager;
-  IdeBuildManager    *build_manager;
-  char               *log_name;
-  GSimpleActionGroup *actions;
-
-  gulong              notify_pipeline_handler;
-  gulong              stopped_handler;
-
-  guint               has_handler : 1;
-};
-
-static void
-set_state (GSimpleAction *action,
-           GVariant      *param,
-           gpointer       user_data)
-{
-  g_simple_action_set_state (action, param);
-}
-
-static gboolean
-get_bool (GbpValgrindWorkbenchAddin *self,
-          const char                *action_name)
-{
-  g_autoptr(GVariant) state = NULL;
-  GAction *action;
-
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-  g_assert (action_name != NULL);
-
-  if (!(action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), action_name)))
-    g_return_val_if_reached (FALSE);
-
-  if (!(state = g_action_get_state (action)))
-    g_return_val_if_reached (FALSE);
-
-  if (!g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
-    g_return_val_if_reached (FALSE);
-
-  return g_variant_get_boolean (state);
-}
-
-static const char *
-get_string (GbpValgrindWorkbenchAddin *self,
-            const char                *action_name)
-{
-  g_autoptr(GVariant) state = NULL;
-  GAction *action;
-
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-  g_assert (action_name != NULL);
-
-  if (!(action = g_action_map_lookup_action (G_ACTION_MAP (self->actions), action_name)))
-    g_return_val_if_reached (NULL);
-
-  if (!(state = g_action_get_state (action)))
-    g_return_val_if_reached (NULL);
-
-  if (!g_variant_is_of_type (state, G_VARIANT_TYPE_STRING))
-    g_return_val_if_reached (NULL);
-
-  return g_variant_get_string (state, NULL);
-}
-
-static void
-gbp_valgrind_workbench_addin_stop_cb (GbpValgrindWorkbenchAddin *self,
-                                      IdeRunManager             *run_manager)
-{
-  g_autoptr(GFile) file = NULL;
-
-  IDE_ENTRY;
-
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-
-  if (self->workbench == NULL || self->log_name == NULL)
-    IDE_EXIT;
-
-  file = g_file_new_for_path (self->log_name);
-  ide_workbench_open_async (self->workbench,
-                            file,
-                            "editorui",
-                            IDE_BUFFER_OPEN_FLAGS_NONE,
-                            NULL, NULL, NULL, NULL);
-  g_clear_pointer (&self->log_name, g_free);
-
-  IDE_EXIT;
-}
-
-static gboolean
-gbp_valgrind_workbench_addin_run_handler_cb (IdeRunContext       *run_context,
-                                             const char * const  *argv,
-                                             const char * const  *env,
-                                             const char          *cwd,
-                                             IdeUnixFDMap        *unix_fd_map,
-                                             gpointer             user_data,
-                                             GError             **error)
-{
-  GbpValgrindWorkbenchAddin *self = user_data;
-  g_autofree char *name = NULL;
-  g_autofree char *track_origins = NULL;
-  g_autofree char *leak_check = NULL;
-  g_autoptr(GString) leak_kinds = NULL;
-  int source_fd;
-  int dest_fd;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_RUN_CONTEXT (run_context));
-  g_assert (IDE_IS_UNIX_FD_MAP (unix_fd_map));
-  g_assert (argv != NULL);
-  g_assert (env != NULL);
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-
-  if (cwd != NULL)
-    ide_run_context_set_cwd (run_context, cwd);
-
-  dest_fd = ide_unix_fd_map_get_max_dest_fd (unix_fd_map) + 1;
-  if (!ide_run_context_merge_unix_fd_map (run_context, unix_fd_map, error))
-    IDE_RETURN (FALSE);
-
-  /* Create a temp file to write to and an FD to access it */
-  if (-1 == (source_fd = g_file_open_tmp ("gnome-builder-valgrind-XXXXXX.txt", &name, error)))
-    IDE_RETURN (FALSE);
-
-  /* Set our FD for valgrind to log to */
-  ide_run_context_take_fd (run_context, source_fd, dest_fd);
-
-  /* Save the filename so we can open it after exiting */
-  g_clear_pointer (&self->log_name, g_free);
-  self->log_name = g_steal_pointer (&name);
-
-  /* Convert action state to command-line arguments */
-  track_origins = g_strdup_printf ("--track-origins=%s", get_bool (self, "track-origins") ? "yes" : "no");
-  leak_check = g_strdup_printf ("--leak-check=%s", get_string (self, "leak-check"));
-
-  leak_kinds = g_string_new (NULL);
-  if (get_bool (self, "leak-kind-definite")) g_string_append (leak_kinds, "definite,");
-  if (get_bool (self, "leak-kind-possible")) g_string_append (leak_kinds, "possible,");
-  if (get_bool (self, "leak-kind-indirect")) g_string_append (leak_kinds, "indirect,");
-  if (get_bool (self, "leak-kind-reachable")) g_string_append (leak_kinds, "reachable,");
-
-  ide_run_context_append_argv (run_context, "valgrind");
-  ide_run_context_append_formatted (run_context, "--log-fd=%d", dest_fd);
-  ide_run_context_append_argv (run_context, leak_check);
-  ide_run_context_append_argv (run_context, track_origins);
-
-  if (leak_kinds->len > 0)
-    {
-      g_string_truncate (leak_kinds, leak_kinds->len-1);
-      ide_run_context_append_formatted (run_context, "--show-leak-kinds=%s", leak_kinds->str);
-    }
-
-  if (env[0] != NULL)
-    {
-      /* If we have to exec "env" to pass environment variables, then we
-       * must follow children to get to our target executable.
-       */
-      ide_run_context_append_argv (run_context, "--trace-children=yes");
-      ide_run_context_append_argv (run_context, "env");
-      ide_run_context_append_args (run_context, env);
-    }
-
-  ide_run_context_append_args (run_context, argv);
-
-  IDE_RETURN (TRUE);
-}
-
-static void
-gbp_valgrind_workbench_addin_run_handler (IdeRunManager *run_manager,
-                                          IdePipeline   *pipeline,
-                                          IdeRunCommand *run_command,
-                                          IdeRunContext *run_context,
-                                          gpointer       user_data)
-{
-  GbpValgrindWorkbenchAddin *self = user_data;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_RUN_MANAGER (run_manager));
-  g_assert (IDE_IS_RUN_CONTEXT (run_context));
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-
-  ide_run_context_push (run_context,
-                        gbp_valgrind_workbench_addin_run_handler_cb,
-                        g_object_ref (self),
-                        g_object_unref);
-
-  IDE_EXIT;
-}
-
-static void
-gbp_valgrind_workbench_addin_notify_pipeline_cb (GbpValgrindWorkbenchAddin *self,
-                                                 GParamSpec                *pspec,
-                                                 IdeBuildManager           *build_manager)
-{
-  IdePipeline *pipeline;
-  IdeRuntime *runtime;
-  gboolean can_handle = FALSE;
-
-  IDE_ENTRY;
-
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_BUILD_MANAGER (build_manager));
-  g_assert (IDE_IS_RUN_MANAGER (self->run_manager));
-
-  if (!(pipeline = ide_build_manager_get_pipeline (build_manager)) ||
-      !(runtime = ide_pipeline_get_runtime (pipeline)) ||
-      !ide_runtime_contains_program_in_path (runtime, "valgrind", NULL))
-    IDE_GOTO (not_found);
-
-  can_handle = TRUE;
-
-not_found:
-  if (can_handle != self->has_handler)
-    {
-      self->has_handler = can_handle;
-
-      if (can_handle)
-        ide_run_manager_add_handler (self->run_manager,
-                                     "valgrind",
-                                     _("Run with Valgrind"),
-                                     "system-run-symbolic",
-                                     gbp_valgrind_workbench_addin_run_handler,
-                                     g_object_ref (self),
-                                     g_object_unref);
-      else
-        ide_run_manager_remove_handler (self->run_manager, "valgrind");
-    }
-
-  IDE_EXIT;
-}
-
-static void
-gbp_valgrind_workbench_addin_project_loaded (IdeWorkbenchAddin *addin,
-                                             IdeProjectInfo    *project_info)
-{
-  GbpValgrindWorkbenchAddin *self = (GbpValgrindWorkbenchAddin *)addin;
-  IdeBuildManager *build_manager;
-  IdeRunManager *run_manager;
-  IdeContext *context;
-
-  IDE_ENTRY;
-
-  g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
-  g_assert (IDE_IS_PROJECT_INFO (project_info));
-  g_assert (IDE_IS_WORKBENCH (self->workbench));
-
-  context = ide_workbench_get_context (self->workbench);
-  build_manager = ide_build_manager_from_context (context);
-  run_manager = ide_run_manager_from_context (context);
-
-  g_set_object (&self->build_manager, build_manager);
-  g_set_object (&self->run_manager, run_manager);
-
-  self->notify_pipeline_handler =
-    g_signal_connect_object (build_manager,
-                             "notify::pipeline",
-                             G_CALLBACK (gbp_valgrind_workbench_addin_notify_pipeline_cb),
-                             self,
-                             G_CONNECT_SWAPPED);
-
-  self->stopped_handler =
-    g_signal_connect_object (run_manager,
-                             "stopped",
-                             G_CALLBACK (gbp_valgrind_workbench_addin_stop_cb),
-                             self,
-                             G_CONNECT_SWAPPED);
-
-  gbp_valgrind_workbench_addin_notify_pipeline_cb (self, NULL, build_manager);
-
-  IDE_EXIT;
-}
-
-static const GActionEntry actions[] = {
-  { "track-origins", NULL, NULL, "true", set_state },
-  { "leak-check", NULL, "s", "'summary'", set_state },
-  { "leak-kind-definite", NULL, NULL, "true", set_state },
-  { "leak-kind-possible", NULL, NULL, "true", set_state },
-  { "leak-kind-indirect", NULL, NULL, "false", set_state },
-  { "leak-kind-reachable", NULL, NULL, "false", set_state },
+  GObject       parent_instance;
+  GActionGroup *actions;
 };
 
 static void
@@ -324,19 +43,29 @@ gbp_valgrind_workbench_addin_load (IdeWorkbenchAddin *addin,
                                    IdeWorkbench      *workbench)
 {
   GbpValgrindWorkbenchAddin *self = (GbpValgrindWorkbenchAddin *)addin;
+  g_autoptr(GSettings) settings = NULL;
+  static const char *keys[] = {
+    "leak-check",
+    "leak-kind-definite",
+    "leak-kind-indirect",
+    "leak-kind-possible",
+    "leak-kind-reachable",
+    "track-origins",
+  };
 
   IDE_ENTRY;
 
   g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_WORKBENCH (workbench));
 
-  self->workbench = workbench;
+  settings = g_settings_new ("org.gnome.builder.valgrind");
+  self->actions = G_ACTION_GROUP (g_simple_action_group_new ());
 
-  self->actions = g_simple_action_group_new ();
-  g_action_map_add_action_entries (G_ACTION_MAP (self->actions),
-                                   actions,
-                                   G_N_ELEMENTS (actions),
-                                   self);
+  for (guint i = 0; i < G_N_ELEMENTS (keys); i++)
+    {
+      g_autoptr(GAction) action = g_settings_create_action (settings, keys[i]);
+      g_action_map_add_action (G_ACTION_MAP (self->actions), action);
+    }
 
   IDE_EXIT;
 }
@@ -352,28 +81,8 @@ gbp_valgrind_workbench_addin_unload (IdeWorkbenchAddin *addin,
   g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_WORKBENCH (workbench));
 
-  if (self->build_manager != NULL)
-    {
-      g_clear_signal_handler (&self->notify_pipeline_handler, self->build_manager);
-      g_clear_object (&self->build_manager);
-    }
-
-  if (self->run_manager != NULL)
-    {
-      g_clear_signal_handler (&self->stopped_handler, self->run_manager);
-
-      if (self->has_handler)
-        ide_run_manager_remove_handler (self->run_manager, "valgrind");
-
-      g_clear_object (&self->run_manager);
-    }
-
-  g_clear_pointer (&self->log_name, g_free);
-
   g_clear_object (&self->actions);
 
-  self->workbench = NULL;
-
   IDE_EXIT;
 }
 
@@ -388,9 +97,10 @@ gbp_valgrind_workbench_addin_workspace_added (IdeWorkbenchAddin *addin,
   g_assert (GBP_IS_VALGRIND_WORKBENCH_ADDIN (self));
   g_assert (IDE_IS_WORKSPACE (workspace));
 
-  gtk_widget_insert_action_group (GTK_WIDGET (workspace),
-                                  "valgrind",
-                                  G_ACTION_GROUP (self->actions));
+  if (IDE_IS_PRIMARY_WORKSPACE (workspace))
+    gtk_widget_insert_action_group (GTK_WIDGET (workspace),
+                                    "valgrind",
+                                    self->actions);
 
   IDE_EXIT;
 }
@@ -416,7 +126,6 @@ workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
 {
   iface->load = gbp_valgrind_workbench_addin_load;
   iface->unload = gbp_valgrind_workbench_addin_unload;
-  iface->project_loaded = gbp_valgrind_workbench_addin_project_loaded;
   iface->workspace_added = gbp_valgrind_workbench_addin_workspace_added;
   iface->workspace_removed = gbp_valgrind_workbench_addin_workspace_removed;
 }
diff --git a/src/plugins/valgrind/meson.build b/src/plugins/valgrind/meson.build
index 72bb792f9..132ba5616 100644
--- a/src/plugins/valgrind/meson.build
+++ b/src/plugins/valgrind/meson.build
@@ -2,6 +2,7 @@ if get_option('plugin_valgrind')
 
 plugins_sources += files([
   'valgrind-plugin.c',
+  'gbp-valgrind-tool.c',
   'gbp-valgrind-workbench-addin.c',
 ])
 
@@ -13,4 +14,6 @@ plugin_valgrind_resources = gnome.compile_resources(
 
 plugins_sources += plugin_valgrind_resources
 
+install_data(['org.gnome.builder.valgrind.gschema.xml'], install_dir: schema_dir)
+
 endif
diff --git a/src/plugins/valgrind/org.gnome.builder.valgrind.gschema.xml 
b/src/plugins/valgrind/org.gnome.builder.valgrind.gschema.xml
new file mode 100644
index 000000000..fe9eb0387
--- /dev/null
+++ b/src/plugins/valgrind/org.gnome.builder.valgrind.gschema.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schemalist>
+  <schema id="org.gnome.builder.valgrind" path="/org/gnome/builder/valgrind/" gettext-domain="gnome-builder">
+    <key name="leak-kind-definite" type="b">
+      <default>true</default>
+    </key>
+    <key name="leak-kind-possible" type="b">
+      <default>true</default>
+    </key>
+    <key name="leak-kind-indirect" type="b">
+      <default>false</default>
+    </key>
+    <key name="leak-kind-reachable" type="b">
+      <default>false</default>
+    </key>
+    <key name="track-origins" type="b">
+      <default>true</default>
+    </key>
+    <key name="leak-check" type="s">
+      <choices>
+        <choice value="no"/>
+        <choice value="summary"/>
+        <choice value="full"/>
+      </choices>
+      <default>'summary'</default>
+    </key>
+  </schema>
+</schemalist>
diff --git a/src/plugins/valgrind/valgrind-plugin.c b/src/plugins/valgrind/valgrind-plugin.c
index d6d0a5715..b3df73279 100644
--- a/src/plugins/valgrind/valgrind-plugin.c
+++ b/src/plugins/valgrind/valgrind-plugin.c
@@ -26,11 +26,15 @@
 
 #include <libide-gui.h>
 
+#include "gbp-valgrind-tool.h"
 #include "gbp-valgrind-workbench-addin.h"
 
 _IDE_EXTERN void
 _gbp_valgrind_register_types (PeasObjectModule *module)
 {
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_RUN_TOOL,
+                                              GBP_TYPE_VALGRIND_TOOL);
   peas_object_module_register_extension_type (module,
                                               IDE_TYPE_WORKBENCH_ADDIN,
                                               GBP_TYPE_VALGRIND_WORKBENCH_ADDIN);


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