[gnome-builder/wip/chergert/debugger: 20/46] mi2: add api to track breakpoints



commit dc403645034160c5109d132ced398a5eea285ebc
Author: Christian Hergert <chergert redhat com>
Date:   Thu Mar 23 21:47:30 2017 -0700

    mi2: add api to track breakpoints

 contrib/mi2/mi2-client.c  |  252 ++++++++++++++++++++++++++++++++++++++++++++-
 contrib/mi2/mi2-client.h  |   54 +++++++---
 contrib/mi2/test-client.c |   50 +++++++++-
 3 files changed, 333 insertions(+), 23 deletions(-)
---
diff --git a/contrib/mi2/mi2-client.c b/contrib/mi2/mi2-client.c
index 184fe6a..8807109 100644
--- a/contrib/mi2/mi2-client.c
+++ b/contrib/mi2/mi2-client.c
@@ -45,6 +45,8 @@ enum {
 };
 
 enum {
+  BREAKPOINT_INSERTED,
+  BREAKPOINT_REMOVED,
   EVENT,
   LOG,
   N_SIGNALS
@@ -162,6 +164,22 @@ mi2_client_class_init (Mi2ClientClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
+  signals [BREAKPOINT_INSERTED] =
+    g_signal_new ("breakpoint-inserted",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (Mi2ClientClass, breakpoint_inserted),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, MI2_TYPE_BREAKPOINT);
+
+  signals [BREAKPOINT_REMOVED] =
+    g_signal_new ("breakpoint-removed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (Mi2ClientClass, breakpoint_removed),
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_INT);
+
   signals [EVENT] =
     g_signal_new ("event",
                   G_TYPE_FROM_CLASS (klass),
@@ -262,15 +280,37 @@ mi2_client_exec_async (Mi2Client           *self,
                                          g_steal_pointer (&task));
 }
 
+/**
+ * mi2_client_exec_finish:
+ * @self: An #Mi2Client
+ * @result: A #GAsyncResult
+ * @reply: (optional) (out) (transfer full): A location for a reply.
+ * @error: a location for a #GError or %NULL
+ *
+ * Completes a request to mi2_client_exec_async(). The reply from the
+ * gdb instance will be provided to @message.
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+ */
 gboolean
-mi2_client_exec_finish (Mi2Client     *self,
-                        GAsyncResult  *result,
-                        GError       **error)
+mi2_client_exec_finish (Mi2Client        *self,
+                        GAsyncResult     *result,
+                        Mi2ReplyMessage **reply,
+                        GError          **error)
 {
+  g_autoptr(Mi2ReplyMessage) local_message = NULL;
+  gboolean ret;
+
   g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
   g_return_val_if_fail (G_IS_TASK (result), FALSE);
 
-  return g_task_propagate_boolean (G_TASK (result), error);
+  local_message = g_task_propagate_pointer (G_TASK (result), error);
+  ret = !!local_message;
+
+  if (reply)
+    *reply = g_steal_pointer (&local_message);
+
+  return ret;
 }
 
 static void
@@ -307,7 +347,7 @@ mi2_client_dispatch (Mi2Client  *self,
           if (mi2_reply_message_check_error (MI2_REPLY_MESSAGE (message), &error))
             g_task_return_error (task, g_steal_pointer (&error));
           else
-            g_task_return_boolean (task, TRUE);
+            g_task_return_pointer (task, g_object_ref (message), g_object_unref);
         }
     }
   else
@@ -380,6 +420,208 @@ mi2_client_stop_listening (Mi2Client *self)
     }
 }
 
+static void
+mi2_client_insert_breakpoint_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  Mi2Client *self = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(Mi2ReplyMessage) message = NULL;
+  Mi2Breakpoint *breakpoint;
+  GVariant *bkpt;
+
+  g_assert (MI2_IS_CLIENT (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!mi2_client_exec_finish (self, result, &message, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  breakpoint = g_task_get_task_data (task);
+
+  bkpt = mi2_message_get_param (MI2_MESSAGE (message), "bkpt");
+
+  if (bkpt != NULL)
+    {
+      GVariantDict dict;
+      const gchar *number = NULL;
+
+      g_variant_dict_init (&dict, bkpt);
+      g_variant_dict_lookup (&dict, "number", "&s", &number);
+      g_variant_dict_clear (&dict);
+
+      mi2_breakpoint_set_id (breakpoint, g_ascii_strtoll (number, NULL, 10));
+    }
+
+  g_signal_emit (self, signals [BREAKPOINT_INSERTED], 0, breakpoint);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * mi2_client_insert_breakpoint_async:
+ * @self: A #Mi2Client
+ * @breakpoint: An #Mi2Breakpoint
+ * @cancellable: (nullable): A #GCancellable or %NULL
+ * @callback: A callback to execute
+ * @user_data: user data for @callback
+ *
+ * Adds a breakpoint at @function. If @filename is specified, the function
+ * will be resolved within that file.
+ *
+ * Call mi2_client_insert_breakpoint_async() to complete the operation.
+ */
+void
+mi2_client_insert_breakpoint_async (Mi2Client           *self,
+                                    Mi2Breakpoint       *breakpoint,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  g_autoptr(Mi2CommandMessage) command = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GString) str = NULL;
+  const gchar *address;
+  const gchar *filename;
+  const gchar *function;
+  const gchar *linespec;
+  gint line_offset;
+
+  g_return_if_fail (MI2_IS_CLIENT (self));
+  g_return_if_fail (MI2_IS_BREAKPOINT (breakpoint));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, mi2_client_insert_breakpoint_async);
+  g_task_set_task_data (task, g_object_ref (breakpoint), g_object_unref);
+
+  str = g_string_new ("-break-insert");
+
+  line_offset = mi2_breakpoint_get_line_offset (breakpoint);
+  linespec = mi2_breakpoint_get_linespec (breakpoint);
+  function = mi2_breakpoint_get_function (breakpoint);
+  filename = mi2_breakpoint_get_filename (breakpoint);
+  address = mi2_breakpoint_get_address (breakpoint);
+
+  if (linespec)
+    g_string_append_printf (str, " %s", linespec);
+
+  if (filename)
+    g_string_append_printf (str, " --source %s", filename);
+
+  if (function)
+    g_string_append_printf (str, " --function %s", function);
+
+  if (line_offset)
+    g_string_append_printf (str, " --line %d", line_offset);
+
+  if (address)
+    g_string_append_printf (str, " %s", address);
+
+  command = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
+                          "command", str,
+                          NULL);
+
+  mi2_client_exec_async (self,
+                         str->str,
+                         cancellable,
+                         mi2_client_insert_breakpoint_cb,
+                         g_steal_pointer (&task));
+}
+
+gint
+mi2_client_insert_breakpoint_finish (Mi2Client     *self,
+                                     GAsyncResult  *result,
+                                     GError       **error)
+{
+  g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_int (G_TASK (result), error);
+}
+
+static void
+mi2_client_remove_breakpoint_cb (GObject      *object,
+                                 GAsyncResult *result,
+                                 gpointer      user_data)
+{
+  Mi2Client *self = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  gint id;
+
+  g_assert (MI2_IS_CLIENT (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  if (!mi2_client_exec_finish (self, result, NULL, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  id = GPOINTER_TO_INT (g_task_get_task_data (task));
+  g_signal_emit (self, signals [BREAKPOINT_REMOVED], 0, id);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * mi2_client_remove_breakpoint_async:
+ * @self: A #Mi2Client
+ * @breakpoint_id: The id of the breakpoint
+ * @cancellable: (nullable): A #GCancellable or %NULL
+ * @callback: A callback to execute
+ * @user_data: user data for @callback
+ *
+ * Removes a breakpoint that was previously added.
+ *
+ * Call mi2_client_remove_breakpoint_finish() to complete the operation.
+ */
+void
+mi2_client_remove_breakpoint_async (Mi2Client           *self,
+                                    gint                 breakpoint_id,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(Mi2CommandMessage) command = NULL;
+  g_autofree gchar *str = NULL;
+
+  g_return_if_fail (MI2_IS_CLIENT (self));
+  g_return_if_fail (breakpoint_id > 0);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, mi2_client_remove_breakpoint_async);
+  g_task_set_task_data (task, GINT_TO_POINTER (breakpoint_id), NULL);
+
+  str = g_strdup_printf ("-break-delete %d", breakpoint_id);
+
+  command = g_object_new (MI2_TYPE_COMMAND_MESSAGE,
+                          "command", str,
+                          NULL);
+
+  mi2_client_exec_async (self,
+                         str,
+                         cancellable,
+                         mi2_client_remove_breakpoint_cb,
+                         g_steal_pointer (&task));
+}
+
+gboolean
+mi2_client_remove_breakpoint_finish (Mi2Client     *self,
+                                     GAsyncResult  *result,
+                                     GError       **error)
+{
+  g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+  g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
 #if 0
 void
 mi2_client_async (Mi2Client           *self,
diff --git a/contrib/mi2/mi2-client.h b/contrib/mi2/mi2-client.h
index 98d5445..56de564 100644
--- a/contrib/mi2/mi2-client.h
+++ b/contrib/mi2/mi2-client.h
@@ -23,8 +23,10 @@
 
 G_BEGIN_DECLS
 
+#include "mi2-breakpoint.h"
 #include "mi2-message.h"
 #include "mi2-event-message.h"
+#include "mi2-reply-message.h"
 
 #define MI2_TYPE_CLIENT (mi2_client_get_type())
 
@@ -34,10 +36,14 @@ struct _Mi2ClientClass
 {
   GObjectClass parent_instance;
 
-  void (*log)   (Mi2Client       *self,
-                 const gchar     *log);
-  void (*event) (Mi2Client       *self,
-                 Mi2EventMessage *message);
+  void (*log)                 (Mi2Client       *self,
+                               const gchar     *log);
+  void (*event)               (Mi2Client       *self,
+                               Mi2EventMessage *message);
+  void (*breakpoint_inserted) (Mi2Client     *client,
+                               Mi2Breakpoint *breakpoint);
+  void (*breakpoint_removed)  (Mi2Client     *client,
+                               gint           breakpoint_id);
 
   gpointer _reserved1;
   gpointer _reserved2;
@@ -57,17 +63,35 @@ struct _Mi2ClientClass
   gpointer _reserved16;
 };
 
-Mi2Client *mi2_client_new             (GIOStream            *stream);
-void       mi2_client_exec_async      (Mi2Client            *self,
-                                       const gchar          *command,
-                                       GCancellable         *cancellable,
-                                       GAsyncReadyCallback   callback,
-                                       gpointer              user_data);
-gboolean   mi2_client_exec_finish     (Mi2Client            *self,
-                                       GAsyncResult         *result,
-                                       GError              **error);
-void       mi2_client_start_listening (Mi2Client            *self);
-void       mi2_client_stop_listening  (Mi2Client            *self);
+Mi2Client *mi2_client_new                      (GIOStream            *stream);
+void       mi2_client_exec_async               (Mi2Client            *self,
+                                                const gchar          *command,
+                                                GCancellable         *cancellable,
+                                                GAsyncReadyCallback   callback,
+                                                gpointer              user_data);
+gboolean   mi2_client_exec_finish              (Mi2Client            *self,
+                                                GAsyncResult         *result,
+                                                Mi2ReplyMessage     **reply,
+                                                GError              **error);
+void       mi2_client_start_listening          (Mi2Client            *self);
+void       mi2_client_stop_listening           (Mi2Client            *self);
+void       mi2_client_insert_breakpoint_async  (Mi2Client            *self,
+                                                Mi2Breakpoint        *breakpoint,
+                                                GCancellable         *cancellable,
+                                                GAsyncReadyCallback   callback,
+                                                gpointer              user_data);
+gint       mi2_client_insert_breakpoint_finish (Mi2Client            *self,
+                                                GAsyncResult         *result,
+                                                GError              **error);
+void       mi2_client_remove_breakpoint_async  (Mi2Client            *self,
+                                                gint                  breakpoint_id,
+                                                GCancellable         *cancellable,
+                                                GAsyncReadyCallback   callback,
+                                                gpointer              user_data);
+gboolean   mi2_client_remove_breakpoint_finish (Mi2Client            *self,
+                                                GAsyncResult         *result,
+                                                GError              **error);
+
 
 G_END_DECLS
 
diff --git a/contrib/mi2/test-client.c b/contrib/mi2/test-client.c
index 045e578..a8fe18e 100644
--- a/contrib/mi2/test-client.c
+++ b/contrib/mi2/test-client.c
@@ -30,7 +30,7 @@ create_io_stream_to_gdb (void)
 
   subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE,
                                  &error,
-                                 "gdb", "--interpreter", "mi2",
+                                 "gdb", "--interpreter", "mi2", "ls",
                                  NULL);
   g_assert_no_error (error);
   g_assert (subprocess);
@@ -66,23 +66,64 @@ event (Mi2Client       *client,
 }
 
 static void
+breakpoint_cb (GObject      *object,
+               GAsyncResult *result,
+               gpointer      user_data)
+{
+  Mi2Client *client = (Mi2Client *)object;
+  g_autoptr(GError) error = NULL;
+  gboolean r;
+
+  g_assert (MI2_IS_CLIENT (client));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  r = mi2_client_insert_breakpoint_finish (client, result, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+}
+
+static void
 stack_info_frame_cb (GObject      *object,
                      GAsyncResult *result,
                      gpointer      user_data)
 {
   Mi2Client *client = (Mi2Client *)object;
   g_autoptr(GError) error = NULL;
+  g_autoptr(Mi2Breakpoint) breakpoint = NULL;
   gboolean r;
 
   g_assert (MI2_IS_CLIENT (client));
   g_assert (G_IS_ASYNC_RESULT (result));
 
-  r = mi2_client_exec_finish (client, result, &error);
+  r = mi2_client_exec_finish (client, result, NULL, &error);
   g_assert_error (error, MI2_ERROR, MI2_ERROR_UNKNOWN_ERROR);
   g_assert_cmpstr (error->message, ==, "No registers.");
   g_assert_cmpint (r, ==, FALSE);
 
-  g_main_loop_quit (main_loop);
+  breakpoint = mi2_breakpoint_new ();
+  mi2_breakpoint_set_function (breakpoint, "main");
+
+  mi2_client_insert_breakpoint_async (client,
+                                      breakpoint,
+                                      NULL,
+                                      breakpoint_cb,
+                                      NULL);
+}
+
+static void
+on_breakpoint_inserted (Mi2Client     *client,
+                        Mi2Breakpoint *breakpoint,
+                        gpointer       user_data)
+{
+  g_print ("breakpoint added: %d\n", mi2_breakpoint_get_id (breakpoint));
+}
+
+static void
+on_breakpoint_removed (Mi2Client *client,
+                       gint       breakpoint_id,
+                       gpointer   user_data)
+{
+  g_print ("breakpoint removed: %d\n", breakpoint_id);
 }
 
 gint
@@ -99,10 +140,13 @@ main (gint argc,
   g_signal_connect (client, "log", G_CALLBACK (log_handler), NULL);
   g_signal_connect (client, "event::thread-group-added", G_CALLBACK (thread_group_added), NULL);
   g_signal_connect (client, "event", G_CALLBACK (event), NULL);
+  g_signal_connect (client, "breakpoint-inserted", G_CALLBACK (on_breakpoint_inserted), NULL);
+  g_signal_connect (client, "breakpoint-removed", G_CALLBACK (on_breakpoint_removed), NULL);
 
   mi2_client_start_listening (client);
 
   mi2_client_exec_async (client,
+                         /* converted to -stack-info-frame */
                          "stack-info-frame",
                          NULL,
                          stack_info_frame_cb,


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