[gnome-builder/wip/chergert/debugger: 82/86] debugger: start on breakpoint-reached signal



commit 10dec30aa2f7e7fe9d130a5bf2f5a3dc397811a2
Author: Christian Hergert <chergert redhat com>
Date:   Tue Apr 4 14:14:02 2017 -0700

    debugger: start on breakpoint-reached signal
    
    We want to jump to the breakpoint location when the debugger stops at
    a location.
    
    We still need more plumbing here, and support for non-source breakpoints
    such as logical address (where we might need to disassemble).

 libide/Makefile.am                             |    2 +
 libide/debugger/ide-breakpoint.c               |  270 ++++++++++++++++++++++++
 libide/debugger/ide-breakpoint.h               |   62 ++++++
 libide/debugger/ide-debug-manager.c            |    2 +
 libide/debugger/ide-debugger-workbench-addin.c |  115 ++++++++++-
 libide/ide-types.h                             |    1 +
 plugins/gdb/gbp-gdb-debugger.c                 |   25 +++
 7 files changed, 472 insertions(+), 5 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index adbd2f3..2d24811 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -49,6 +49,7 @@ libide_1_0_la_public_headers =                                              \
        buildsystem/ide-configuration-provider.h                            \
        buildsystem/ide-environment-variable.h                              \
        buildsystem/ide-environment.h                                       \
+       debugger/ide-breakpoint.h                                           \
        debugger/ide-debugger.h                                             \
        debugger/ide-debug-manager.h                                        \
        debugger/ide-debugger-breakpoints.h                                 \
@@ -237,6 +238,7 @@ libide_1_0_la_public_sources =                                              \
        buildsystem/ide-configuration-provider.c                            \
        buildsystem/ide-environment-variable.c                              \
        buildsystem/ide-environment.c                                       \
+       debugger/ide-breakpoint.c                                           \
        debugger/ide-debugger.c                                             \
        debugger/ide-debug-manager.c                                        \
        debugger/ide-debugger-breakpoints.c                                 \
diff --git a/libide/debugger/ide-breakpoint.c b/libide/debugger/ide-breakpoint.c
new file mode 100644
index 0000000..adaaa54
--- /dev/null
+++ b/libide/debugger/ide-breakpoint.c
@@ -0,0 +1,270 @@
+/* ide-breakpoint.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-breakpoint"
+
+#include "debugger/ide-breakpoint.h"
+
+typedef struct
+{
+  gchar *id;
+  GFile *file;
+  guint  line;
+  guint  enabled : 1;
+} IdeBreakpointPrivate;
+
+enum {
+  PROP_0,
+  PROP_ENABLED,
+  PROP_FILE,
+  PROP_ID,
+  PROP_LINE,
+  N_PROPS
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBreakpoint, ide_breakpoint, G_TYPE_OBJECT)
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_breakpoint_finalize (GObject *object)
+{
+  IdeBreakpoint *self = (IdeBreakpoint *)object;
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_clear_object (&priv->file);
+  g_clear_pointer (&priv->id, g_free);
+
+  G_OBJECT_CLASS (ide_breakpoint_parent_class)->finalize (object);
+}
+
+static void
+ide_breakpoint_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  IdeBreakpoint *self = IDE_BREAKPOINT (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      g_value_set_boolean (value, ide_breakpoint_get_enabled (self));
+      break;
+
+    case PROP_FILE:
+      g_value_set_object (value, ide_breakpoint_get_file (self));
+      break;
+
+    case PROP_ID:
+      g_value_set_string (value, ide_breakpoint_get_id (self));
+      break;
+
+    case PROP_LINE:
+      g_value_set_uint (value, ide_breakpoint_get_line (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_breakpoint_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  IdeBreakpoint *self = IDE_BREAKPOINT (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      ide_breakpoint_set_enabled (self, g_value_get_boolean (value));
+      break;
+
+    case PROP_FILE:
+      ide_breakpoint_set_file (self, g_value_get_object (value));
+      break;
+
+    case PROP_ID:
+      ide_breakpoint_set_id (self, g_value_get_string (value));
+      break;
+
+    case PROP_LINE:
+      ide_breakpoint_set_line (self, g_value_get_uint (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_breakpoint_class_init (IdeBreakpointClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_breakpoint_finalize;
+  object_class->get_property = ide_breakpoint_get_property;
+  object_class->set_property = ide_breakpoint_set_property;
+
+  properties [PROP_ID] =
+    g_param_spec_string ("id",
+                         "Id",
+                         "Identifier for the breakpoint",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_FILE] =
+    g_param_spec_object ("file",
+                         "File",
+                         "The file for the breakpoint",
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_LINE] =
+    g_param_spec_uint ("line",
+                       "Line",
+                       "The line number of the breakpoint",
+                       0,
+                       G_MAXUINT,
+                       0,
+                       (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_ENABLED] =
+    g_param_spec_boolean ("enabled",
+                          "Enabled",
+                          "If the breakpoint is enabled",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_breakpoint_init (IdeBreakpoint *self)
+{
+}
+
+const gchar *
+ide_breakpoint_get_id (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), NULL);
+
+  return priv->id;
+}
+
+void
+ide_breakpoint_set_id (IdeBreakpoint *self,
+                       const gchar   *id)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  if (g_strcmp0 (priv->id, id) != 0)
+    {
+      g_free (priv->id);
+      priv->id = g_strdup (id);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ID]);
+    }
+}
+
+/**
+ * ide_breakpoint_get_file:
+ *
+ * Gets the file containing the breakpoint, or %NULL.
+ *
+ * Returns: (transfer none) (nullable): A #GFile or %NULL.
+ */
+GFile *
+ide_breakpoint_get_file (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), NULL);
+
+  return priv->file;
+}
+
+void
+ide_breakpoint_set_file (IdeBreakpoint *self,
+                         GFile         *file)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  if (g_set_object (&priv->file, file))
+    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_FILE]);
+}
+
+guint
+ide_breakpoint_get_line (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), 0);
+
+  return priv->line;
+}
+
+void
+ide_breakpoint_set_line (IdeBreakpoint *self,
+                         guint          line)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  if (priv->line != line)
+    {
+      priv->line = line;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LINE]);
+    }
+}
+
+gboolean
+ide_breakpoint_get_enabled (IdeBreakpoint *self)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_BREAKPOINT (self), FALSE);
+
+  return priv->enabled;
+}
+
+void
+ide_breakpoint_set_enabled (IdeBreakpoint *self,
+                            gboolean       enabled)
+{
+  IdeBreakpointPrivate *priv = ide_breakpoint_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_BREAKPOINT (self));
+
+  enabled = !!enabled;
+
+  if (priv->enabled != enabled)
+    {
+      priv->enabled = enabled;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ENABLED]);
+    }
+}
diff --git a/libide/debugger/ide-breakpoint.h b/libide/debugger/ide-breakpoint.h
new file mode 100644
index 0000000..72cd7c2
--- /dev/null
+++ b/libide/debugger/ide-breakpoint.h
@@ -0,0 +1,62 @@
+/* ide-breakpoint.h
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BREAKPOINT_H
+#define IDE_BREAKPOINT_H
+
+#include <gio/gio.h>
+
+#include "ide-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BREAKPOINT (ide_breakpoint_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeBreakpoint, ide_breakpoint, IDE, BREAKPOINT, GObject)
+
+struct _IdeBreakpointClass
+{
+  GObjectClass parent_class;
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
+};
+
+IdeBreakpoint *ide_breakpoint_new         (void);
+const gchar   *ide_breakpoint_get_id      (IdeBreakpoint *self);
+void           ide_breakpoint_set_id      (IdeBreakpoint *self,
+                                           const gchar   *id);
+GFile         *ide_breakpoint_get_file    (IdeBreakpoint *self);
+void           ide_breakpoint_set_file    (IdeBreakpoint *self,
+                                           GFile         *file);
+guint          ide_breakpoint_get_line    (IdeBreakpoint *self);
+void           ide_breakpoint_set_line    (IdeBreakpoint *self,
+                                           guint          line);
+gboolean       ide_breakpoint_get_enabled (IdeBreakpoint *self);
+void           ide_breakpoint_set_enabled (IdeBreakpoint *self,
+                                           gboolean       enabled);
+
+G_END_DECLS
+
+#endif /* IDE_BREAKPOINT_H */
diff --git a/libide/debugger/ide-debug-manager.c b/libide/debugger/ide-debug-manager.c
index 027f060..5257a45 100644
--- a/libide/debugger/ide-debug-manager.c
+++ b/libide/debugger/ide-debug-manager.c
@@ -24,6 +24,7 @@
 
 #include "ide-debug.h"
 
+#include "debugger/ide-breakpoint.h"
 #include "debugger/ide-debug-manager.h"
 #include "debugger/ide-debugger.h"
 #include "plugins/ide-extension-util.h"
@@ -59,6 +60,7 @@ enum {
 enum {
   BREAKPOINT_ADDED,
   BREAKPOINT_REMOVED,
+  BREAKPOINT_REACHED,
   N_SIGNALS
 };
 
diff --git a/libide/debugger/ide-debugger-workbench-addin.c b/libide/debugger/ide-debugger-workbench-addin.c
index 93d46aa..d5c0607 100644
--- a/libide/debugger/ide-debugger-workbench-addin.c
+++ b/libide/debugger/ide-debugger-workbench-addin.c
@@ -20,6 +20,10 @@
 
 #include <glib/gi18n.h>
 
+#include <egg-signal-group.h>
+#include <gtksourceview/gtksource.h>
+
+#include "debugger/ide-breakpoint.h"
 #include "debugger/ide-debug-manager.h"
 #include "debugger/ide-debugger-controls.h"
 #include "debugger/ide-debugger-perspective.h"
@@ -33,6 +37,7 @@ struct _IdeDebuggerWorkbenchAddin
   IdeDebuggerControls    *controls;
   IdeWorkbenchMessage    *message;
   IdeDebuggerPerspective *perspective;
+  EggSignalGroup         *debug_manager_signals;
 };
 
 static void
@@ -107,6 +112,36 @@ controls_notify_child_revealed (IdeDebuggerWorkbenchAddin *self,
 }
 
 static void
+debug_manager_breakpoint_reached (IdeDebuggerWorkbenchAddin *self,
+                                  IdeBreakpoint             *breakpoint,
+                                  IdeDebugger               *debugger)
+{
+  g_autoptr(IdeUri) uri = NULL;
+  GFile *file;
+  guint line;
+
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_BREAKPOINT (breakpoint));
+  g_assert (IDE_IS_DEBUGGER (debugger));
+
+  if (NULL == (file = ide_breakpoint_get_file (breakpoint)) ||
+      NULL == (uri = ide_uri_new_from_file (file)))
+    return;
+
+  if (0 != (line = ide_breakpoint_get_line (breakpoint)))
+    {
+      g_autofree gchar *fragment = g_strdup_printf ("L%u", line);
+      ide_uri_set_fragment (uri, fragment);
+    }
+
+  ide_workbench_addin_open_async (IDE_WORKBENCH_ADDIN (self),
+                                  uri,
+                                  NULL,
+                                  IDE_WORKBENCH_OPEN_FLAGS_NONE,
+                                  NULL, NULL, NULL);
+}
+
+static void
 ide_debugger_workbench_addin_load (IdeWorkbenchAddin *addin,
                                    IdeWorkbench      *workbench)
 {
@@ -131,17 +166,26 @@ ide_debugger_workbench_addin_load (IdeWorkbenchAddin *addin,
 
   headerbar = ide_workbench_get_headerbar (workbench);
 
+  self->debug_manager_signals = egg_signal_group_new (IDE_TYPE_DEBUG_MANAGER);
+
+  egg_signal_group_connect_object (self->debug_manager_signals,
+                                   "notify::active",
+                                   G_CALLBACK (debug_manager_notify_active),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+
+  egg_signal_group_connect_object (self->debug_manager_signals,
+                                   "breakpoint-reached",
+                                   G_CALLBACK (debug_manager_breakpoint_reached),
+                                   self,
+                                   G_CONNECT_SWAPPED);
+
   self->controls = g_object_new (IDE_TYPE_DEBUGGER_CONTROLS,
                                  "transition-duration", 500,
                                  "transition-type", GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT,
                                  "reveal-child", FALSE,
                                  "visible", FALSE,
                                  NULL);
-  g_signal_connect_object (debug_manager,
-                           "notify::active",
-                           G_CALLBACK (debug_manager_notify_active),
-                           self,
-                           G_CONNECT_SWAPPED);
   g_signal_connect_object (self->controls,
                            "notify::child-revealed",
                            G_CALLBACK (controls_notify_child_revealed),
@@ -191,6 +235,8 @@ ide_debugger_workbench_addin_unload (IdeWorkbenchAddin *addin,
   context = ide_workbench_get_context (workbench);
   run_manager = ide_context_get_run_manager (context);
 
+  g_clear_object (&self->debug_manager_signals);
+
   /* Remove the handler to initiate the debugger */
   ide_run_manager_remove_handler (run_manager, "debugger");
 
@@ -208,10 +254,69 @@ ide_debugger_workbench_addin_unload (IdeWorkbenchAddin *addin,
 }
 
 static void
+ide_debugger_workbench_addin_open_async (IdeWorkbenchAddin     *addin,
+                                         IdeUri                *uri,
+                                         const gchar           *content_type,
+                                         IdeWorkbenchOpenFlags  flags,
+                                         GCancellable          *cancellable,
+                                         GAsyncReadyCallback    callback,
+                                         gpointer               user_data)
+{
+  IdeDebuggerWorkbenchAddin *self = (IdeDebuggerWorkbenchAddin *)addin;
+  g_autoptr(GtkSourceFileLoader) loader = NULL;
+  g_autoptr(GtkSourceBuffer) buffer = NULL;
+  g_autoptr(GtkSourceFile) sfile = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) file = NULL;
+  GtkSourceView *view;
+
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (self));
+  g_assert (uri != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_debugger_workbench_addin_open_async);
+
+  file = ide_uri_to_file (uri);
+
+  buffer = gtk_source_buffer_new (NULL);
+  view = g_object_new (GTK_SOURCE_TYPE_VIEW,
+                       "buffer", buffer,
+                       "visible", TRUE,
+                       NULL);
+
+  sfile = gtk_source_file_new ();
+  gtk_source_file_set_location (sfile, file);
+
+  loader = gtk_source_file_loader_new (buffer, sfile);
+  gtk_source_file_loader_load_async (loader,
+                                     G_PRIORITY_LOW,
+                                     cancellable,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL,
+                                     NULL);
+}
+
+static gboolean
+ide_debugger_workbench_addin_open_finish (IdeWorkbenchAddin  *addin,
+                                          GAsyncResult       *result,
+                                          GError            **error)
+{
+  g_assert (IDE_IS_DEBUGGER_WORKBENCH_ADDIN (addin));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
 workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
 {
   iface->load = ide_debugger_workbench_addin_load;
   iface->unload = ide_debugger_workbench_addin_unload;
+  iface->open_async = ide_debugger_workbench_addin_open_async;
+  iface->open_finish = ide_debugger_workbench_addin_open_finish;
 }
 
 G_DEFINE_TYPE_WITH_CODE (IdeDebuggerWorkbenchAddin, ide_debugger_workbench_addin, G_TYPE_OBJECT,
diff --git a/libide/ide-types.h b/libide/ide-types.h
index 8643171..206d78d 100644
--- a/libide/ide-types.h
+++ b/libide/ide-types.h
@@ -51,6 +51,7 @@ typedef struct _IdeConfigurationManager        IdeConfigurationManager;
 typedef struct _IdeContext                     IdeContext;
 
 typedef struct _IdeDebugger                    IdeDebugger;
+typedef struct _IdeBreakpoint                  IdeBreakpoint;
 typedef struct _IdeDebugManager                IdeDebugManager;
 
 typedef struct _IdeDevice                      IdeDevice;
diff --git a/plugins/gdb/gbp-gdb-debugger.c b/plugins/gdb/gbp-gdb-debugger.c
index a9cb01c..f183674 100644
--- a/plugins/gdb/gbp-gdb-debugger.c
+++ b/plugins/gdb/gbp-gdb-debugger.c
@@ -328,6 +328,8 @@ gbp_gdb_debugger_on_client_stopped (GbpGdbDebugger  *self,
                                     Mi2Client       *client)
 {
   IdeDebuggerStopReason translated;
+  g_autoptr(IdeSourceLocation) location = NULL;
+  GVariant *fra;
 
   IDE_ENTRY;
 
@@ -357,6 +359,29 @@ gbp_gdb_debugger_on_client_stopped (GbpGdbDebugger  *self,
       break;
     }
 
+  if (NULL != (fra = mi2_message_get_param (MI2_MESSAGE (message), "frame")) &&
+      g_variant_is_of_type (fra, G_VARIANT_TYPE ("a{sv}")))
+    {
+      GVariantDict dict;
+      const gchar *filename = NULL;
+      const gchar *line = NULL;
+
+      g_variant_dict_init (&dict, fra);
+      g_variant_dict_lookup (&dict, "fullname", "&s", &filename);
+      g_variant_dict_lookup (&dict, "line", "&s", &line);
+
+      if (filename != NULL && line != NULL)
+        {
+          IdeContext *context = ide_object_get_context (IDE_OBJECT (self));
+          g_autoptr(GFile) gfile = g_file_new_for_path (filename);
+          g_autoptr(IdeFile) ifile = ide_file_new (context, gfile);
+
+          location = ide_source_location_new (ifile,
+                                              g_ascii_strtoll (line, NULL, 10),
+                                              0, 0);
+        }
+    }
+
   gbp_gdb_debugger_notify_properties (self);
 
   ide_debugger_emit_stopped (IDE_DEBUGGER (self), translated, NULL);


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