gem-graph-client/libide/debugger/ide-debugger.c

2076 lines
64 KiB
C

/* ide-debugger.c
*
* Copyright 2017-2019 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 "ide-debugger"
#include "config.h"
#include "ide-debugger.h"
#include "ide-debugger-address-map-private.h"
#include "ide-debugger-private.h"
/**
* SECTION:ide-debugger
* @title: IdeDebugger
* @short_description: Base class for debugger implementations
*
* The IdeDebugger abstract base class is used by debugger implementations.
* They should bridge their backend-specific features into those supported
* by the API using the series of "emit" functions provided as part of
* this class.
*
* For example, when the inferior creates a new thread, the debugger
* implementation should call ide_debugger_emit_thread_added().
*
* Since: 3.32
*/
typedef struct
{
gchar *display_name;
GListStore *breakpoints;
GListStore *threads;
GListStore *thread_groups;
IdeDebuggerThread *selected;
IdeDebuggerAddressMap *map;
guint has_started : 1;
guint is_running : 1;
} IdeDebuggerPrivate;
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeDebugger, ide_debugger, IDE_TYPE_OBJECT,
G_ADD_PRIVATE (IdeDebugger)
G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
_ide_debugger_class_init_actions))
enum {
PROP_0,
PROP_DISPLAY_NAME,
PROP_SELECTED_THREAD,
N_PROPS
};
enum {
LOG,
THREAD_GROUP_ADDED,
THREAD_GROUP_EXITED,
THREAD_GROUP_REMOVED,
THREAD_GROUP_STARTED,
THREAD_ADDED,
THREAD_REMOVED,
THREAD_SELECTED,
BREAKPOINT_ADDED,
BREAKPOINT_MODIFIED,
BREAKPOINT_REMOVED,
RUNNING,
STOPPED,
LIBRARY_LOADED,
LIBRARY_UNLOADED,
N_SIGNALS
};
static GParamSpec *properties [N_PROPS];
static guint signals [N_SIGNALS];
static void
ide_debugger_real_breakpoint_added (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_debug ("Added breakpoint %s",
ide_debugger_breakpoint_get_id (breakpoint));
g_list_store_insert_sorted (priv->breakpoints,
breakpoint,
(GCompareDataFunc)ide_debugger_breakpoint_compare,
NULL);
}
static void
ide_debugger_real_breakpoint_removed (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
guint n_items;
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_debug ("Removed breakpoint %s",
ide_debugger_breakpoint_get_id (breakpoint));
n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->breakpoints));
for (guint i = 0; i < n_items; i++)
{
g_autoptr(IdeDebuggerBreakpoint) element = NULL;
element = g_list_model_get_item (G_LIST_MODEL (priv->breakpoints), i);
g_assert (element != NULL);
g_assert (IDE_IS_DEBUGGER_BREAKPOINT (element));
if (breakpoint == element)
break;
if (ide_debugger_breakpoint_compare (breakpoint, element) == 0)
{
g_list_store_remove (priv->breakpoints, i);
break;
}
}
}
static void
ide_debugger_real_breakpoint_modified (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_debug ("Modified breakpoint %s (%s)",
ide_debugger_breakpoint_get_id (breakpoint),
ide_debugger_breakpoint_get_enabled (breakpoint) ? "enabled" : "disabled");
/*
* If we add API to GListStore, we could make this a single
* operation instead of 2 signal emissions.
*/
ide_debugger_real_breakpoint_removed (self, breakpoint);
ide_debugger_real_breakpoint_added (self, breakpoint);
}
static void
ide_debugger_real_thread_group_added (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_debug ("Added thread group %s",
ide_debugger_thread_group_get_id (thread_group));
g_list_store_insert_sorted (priv->thread_groups,
thread_group,
(GCompareDataFunc)ide_debugger_thread_group_compare,
NULL);
}
static void
ide_debugger_real_thread_group_removed (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
guint n_items;
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_debug ("Removed thread group %s",
ide_debugger_thread_group_get_id (thread_group));
n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->thread_groups));
for (guint i = 0; i < n_items; i++)
{
g_autoptr(IdeDebuggerThreadGroup) element = NULL;
element = g_list_model_get_item (G_LIST_MODEL (priv->thread_groups), i);
g_assert (element != NULL);
g_assert (IDE_IS_DEBUGGER_THREAD_GROUP (element));
if (thread_group == element)
break;
if (ide_debugger_thread_group_compare (thread_group, element) == 0)
{
g_list_store_remove (priv->thread_groups, i);
break;
}
}
}
static void
ide_debugger_real_thread_added (IdeDebugger *self,
IdeDebuggerThread *thread)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_THREAD (thread));
g_debug ("Added thread %s", ide_debugger_thread_get_id (thread));
g_list_store_insert_sorted (priv->threads,
thread,
(GCompareDataFunc)ide_debugger_thread_compare,
NULL);
}
static void
ide_debugger_real_thread_removed (IdeDebugger *self,
IdeDebuggerThread *thread)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
guint n_items;
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_THREAD (thread));
g_debug ("Removed thread %s", ide_debugger_thread_get_id (thread));
n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->threads));
for (guint i = 0; i < n_items; i++)
{
g_autoptr(IdeDebuggerThread) element = NULL;
element = g_list_model_get_item (G_LIST_MODEL (priv->threads), i);
g_assert (element != NULL);
g_assert (IDE_IS_DEBUGGER_THREAD (element));
if (thread == element)
break;
if (ide_debugger_thread_compare (thread, element) == 0)
{
g_list_store_remove (priv->threads, i);
break;
}
}
}
static void
ide_debugger_real_running (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
priv->is_running = TRUE;
priv->has_started = TRUE;
_ide_debugger_update_actions (self);
}
static void
ide_debugger_real_stopped (IdeDebugger *self,
IdeDebuggerStopReason stop_reason,
IdeDebuggerBreakpoint *breakpoint)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_STOP_REASON (stop_reason));
g_assert (!breakpoint || IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
/* We might need to eventually track this by thread group */
priv->is_running = FALSE;
if (IDE_DEBUGGER_STOP_IS_TERMINAL (stop_reason))
priv->has_started = FALSE;
_ide_debugger_update_actions (self);
}
static void
ide_debugger_real_thread_selected (IdeDebugger *self,
IdeDebuggerThread *thread)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_THREAD (thread));
if (g_set_object (&priv->selected, thread))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED_THREAD]);
}
static void
ide_debugger_finalize (GObject *object)
{
IdeDebugger *self = (IdeDebugger *)object;
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_clear_pointer (&priv->map, ide_debugger_address_map_free);
g_clear_pointer (&priv->display_name, g_free);
g_clear_object (&priv->breakpoints);
g_clear_object (&priv->threads);
g_clear_object (&priv->thread_groups);
g_clear_object (&priv->selected);
G_OBJECT_CLASS (ide_debugger_parent_class)->finalize (object);
}
static gboolean
ide_debugger_real_get_can_move (IdeDebugger *self,
IdeDebuggerMovement movement)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (IDE_IS_DEBUGGER_MOVEMENT (movement), FALSE);
switch (movement)
{
case IDE_DEBUGGER_MOVEMENT_START:
return !ide_debugger_get_is_running (self);
case IDE_DEBUGGER_MOVEMENT_FINISH:
case IDE_DEBUGGER_MOVEMENT_CONTINUE:
case IDE_DEBUGGER_MOVEMENT_STEP_IN:
case IDE_DEBUGGER_MOVEMENT_STEP_OVER:
return _ide_debugger_get_has_started (self) &&
!ide_debugger_get_is_running (self);
default:
g_return_val_if_reached (FALSE);
}
}
static void
ide_debugger_real_library_loaded (IdeDebugger *self,
IdeDebuggerLibrary *library)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
IdeDebuggerAddressMapEntry entry = { 0 };
GPtrArray *ranges;
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_LIBRARY (library));
/* We don't yet have this information */
entry.offset = 0;
entry.filename = ide_debugger_library_get_target_name (library);
ranges = ide_debugger_library_get_ranges (library);
if (ranges != NULL)
{
for (guint i = 0; i < ranges->len; i++)
{
const IdeDebuggerAddressRange *range = g_ptr_array_index (ranges, i);
entry.start = range->from;
entry.end = range->to;
ide_debugger_address_map_insert (priv->map, &entry);
}
}
}
static void
ide_debugger_real_library_unloaded (IdeDebugger *self,
IdeDebuggerLibrary *library)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
GPtrArray *ranges;
g_assert (IDE_IS_DEBUGGER (self));
g_assert (IDE_IS_DEBUGGER_LIBRARY (library));
ranges = ide_debugger_library_get_ranges (library);
if (ranges != NULL)
{
for (guint i = 0; i < ranges->len; i++)
{
const IdeDebuggerAddressRange *range = g_ptr_array_index (ranges, i);
ide_debugger_address_map_remove (priv->map, range->from);
}
}
}
static void
ide_debugger_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeDebugger *self = IDE_DEBUGGER (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
g_value_set_string (value, ide_debugger_get_display_name (self));
break;
case PROP_SELECTED_THREAD:
g_value_set_object (value, ide_debugger_get_selected_thread (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_debugger_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeDebugger *self = IDE_DEBUGGER (object);
switch (prop_id)
{
case PROP_DISPLAY_NAME:
ide_debugger_set_display_name (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_debugger_class_init (IdeDebuggerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ide_debugger_finalize;
object_class->get_property = ide_debugger_get_property;
object_class->set_property = ide_debugger_set_property;
klass->breakpoint_added = ide_debugger_real_breakpoint_added;
klass->breakpoint_modified = ide_debugger_real_breakpoint_modified;
klass->breakpoint_removed = ide_debugger_real_breakpoint_removed;
klass->disassemble_async = _ide_debugger_real_disassemble_async;
klass->disassemble_finish = _ide_debugger_real_disassemble_finish;
klass->get_can_move = ide_debugger_real_get_can_move;
klass->interrupt_async = _ide_debugger_real_interrupt_async;
klass->interrupt_finish = _ide_debugger_real_interrupt_finish;
klass->library_loaded = ide_debugger_real_library_loaded;
klass->library_unloaded = ide_debugger_real_library_unloaded;
klass->list_frames_async = _ide_debugger_real_list_frames_async;
klass->list_frames_finish = _ide_debugger_real_list_frames_finish;
klass->list_locals_async = _ide_debugger_real_list_locals_async;
klass->list_locals_finish = _ide_debugger_real_list_locals_finish;
klass->list_params_async = _ide_debugger_real_list_params_async;
klass->list_params_finish = _ide_debugger_real_list_params_finish;
klass->list_registers_async = _ide_debugger_real_list_registers_async;
klass->list_registers_finish = _ide_debugger_real_list_registers_finish;
klass->modify_breakpoint_async = _ide_debugger_real_modify_breakpoint_async;
klass->modify_breakpoint_finish = _ide_debugger_real_modify_breakpoint_finish;
klass->running = ide_debugger_real_running;
klass->send_signal_async = _ide_debugger_real_send_signal_async;
klass->send_signal_finish = _ide_debugger_real_send_signal_finish;
klass->stopped = ide_debugger_real_stopped;
klass->thread_added = ide_debugger_real_thread_added;
klass->thread_group_added = ide_debugger_real_thread_group_added;
klass->thread_group_removed = ide_debugger_real_thread_group_removed;
klass->thread_removed = ide_debugger_real_thread_removed;
klass->thread_selected = ide_debugger_real_thread_selected;
klass->interpret_async = _ide_debugger_real_interpret_async;
klass->interpret_finish = _ide_debugger_real_interpret_finish;
/**
* IdeDebugger:display-name:
*
* The "display-name" property is used by UI to when it is necessary
* to display the name of the debugger. You might set this to "GNU Debugger"
* or "Python Debugger", etc.
*
* Since: 3.32
*/
properties [PROP_DISPLAY_NAME] =
g_param_spec_string ("display-name",
"Display Name",
"The name of the debugger to use in various UI components",
NULL,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* IdeDebugger:selected-thread:
*
* The currently selected thread.
*
* Since: 3.32
*/
properties [PROP_SELECTED_THREAD] =
g_param_spec_object ("selected-thread",
"Selected Thread",
"The currently selected thread",
IDE_TYPE_DEBUGGER_THREAD,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
/**
* IdeDebugger::log:
* @self: An #IdeDebugger.
* @stream: the stream to append to.
* @content: the contents for the stream.
*
* The "log" signal is emitted when there is new content to be
* appended to one of the streams.
*
* Since: 3.32
*/
signals [LOG] =
g_signal_new ("log",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, log),
NULL, NULL, NULL,
G_TYPE_NONE,
2,
IDE_TYPE_DEBUGGER_STREAM,
G_TYPE_BYTES | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* IdeDebugger::thread-group-added:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* This signal is emitted when a thread-group has been added.
*
* Since: 3.32
*/
signals [THREAD_GROUP_ADDED] =
g_signal_new ("thread-group-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_group_added),
NULL, NULL, NULL,
G_TYPE_NONE,
1,
IDE_TYPE_DEBUGGER_THREAD_GROUP);
/**
* IdeDebugger::thread-group-removed:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* This signal is emitted when a thread-group has been removed.
*
* Since: 3.32
*/
signals [THREAD_GROUP_REMOVED] =
g_signal_new ("thread-group-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_group_removed),
NULL, NULL, NULL,
G_TYPE_NONE,
1,
IDE_TYPE_DEBUGGER_THREAD_GROUP);
/**
* IdeDebugger::thread-group-started:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* This signal is emitted when a thread-group has been started.
*
* Since: 3.32
*/
signals [THREAD_GROUP_STARTED] =
g_signal_new ("thread-group-started",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_group_started),
NULL, NULL, NULL,
G_TYPE_NONE,
1,
IDE_TYPE_DEBUGGER_THREAD_GROUP);
/**
* IdeDebugger::thread-group-exited:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* This signal is emitted when a thread-group has exited.
*
* Since: 3.32
*/
signals [THREAD_GROUP_EXITED] =
g_signal_new ("thread-group-exited",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_group_exited),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_THREAD_GROUP);
/**
* IdeDebugger::thread-added:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* The signal is emitted when a thread is added to the inferior.
*
* Since: 3.32
*/
signals [THREAD_ADDED] =
g_signal_new ("thread-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_added),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_THREAD);
/**
* IdeDebugger::thread-removed:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* The signal is emitted when a thread is removed from the inferior.
*
* Since: 3.32
*/
signals [THREAD_REMOVED] =
g_signal_new ("thread-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_removed),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_THREAD);
/**
* IdeDebugger::thread-selected:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* The signal is emitted when a thread is selected in the debugger.
*
* Since: 3.32
*/
signals [THREAD_SELECTED] =
g_signal_new ("thread-selected",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, thread_selected),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_THREAD);
/**
* IdeDebugger::breakpoint-added:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* The "breakpoint-added" signal is emitted when a breakpoint has been
* added to the debugger.
*
* Since: 3.32
*/
signals [BREAKPOINT_ADDED] =
g_signal_new ("breakpoint-added",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, breakpoint_added),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_BREAKPOINT);
/**
* IdeDebugger::breakpoint-removed:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* The "breakpoint-removed" signal is emitted when a breakpoint has been
* removed from the debugger.
*
* Since: 3.32
*/
signals [BREAKPOINT_REMOVED] =
g_signal_new ("breakpoint-removed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, breakpoint_removed),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_BREAKPOINT);
/**
* IdeDebugger::breakpoint-modified:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* The "breakpoint-modified" signal is emitted when a breakpoint has been
* modified by the debugger.
*
* Since: 3.32
*/
signals [BREAKPOINT_MODIFIED] =
g_signal_new ("breakpoint-modified",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, breakpoint_modified),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_BREAKPOINT);
/**
* IdeDebugger::running:
* @self: a #IdeDebugger
*
* This signal is emitted when the debugger starts or resumes executing
* the inferior.
*
* Since: 3.32
*/
signals [RUNNING] =
g_signal_new ("running",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, running),
NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* IdeDebugger::stopped:
* @self: a #IdeDebugger
* @stop_reason: An #IdeDebuggerStopReason
* @breakpoint: (nullable): An #IdeDebuggerBreakpoint if any
*
* This signal is emitted when the debugger has stopped executing the
* inferior for a variety of reasons.
*
* If possible, the debugger implementation will provide the breakpoint of
* the location the debugger stopped. That location may not always be
* representable by source in the project (such as memory address based
* breakpoints).
*
* Since: 3.32
*/
signals [STOPPED] =
g_signal_new ("stopped",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, stopped),
NULL, NULL, NULL,
G_TYPE_NONE,
2,
IDE_TYPE_DEBUGGER_STOP_REASON,
IDE_TYPE_DEBUGGER_BREAKPOINT);
/**
* IdeDebugger::library-loaded:
* @self: An #IdeDebugger
* @library: An #IdeDebuggerLibrary
*
* This signal is emitted when a library has been loaded by the debugger.
*
* Since: 3.32
*/
signals [LIBRARY_LOADED] =
g_signal_new ("library-loaded",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, library_loaded),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_LIBRARY);
/**
* IdeDebugger::library-unloaded:
* @self: An #IdeDebugger
* @library: An #IdeDebuggerLibrary
*
* This signal is emitted when a library has been unloaded by the debugger.
* Generally, this means that the library was a module and loaded in such a
* way that allowed unloading.
*
* Since: 3.32
*/
signals [LIBRARY_UNLOADED] =
g_signal_new ("library-unloaded",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (IdeDebuggerClass, library_unloaded),
NULL, NULL, NULL,
G_TYPE_NONE, 1, IDE_TYPE_DEBUGGER_LIBRARY);
}
static void
ide_debugger_init (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
priv->breakpoints = g_list_store_new (IDE_TYPE_DEBUGGER_BREAKPOINT);
priv->map = ide_debugger_address_map_new ();
priv->thread_groups = g_list_store_new (IDE_TYPE_DEBUGGER_THREAD_GROUP);
priv->threads = g_list_store_new (IDE_TYPE_DEBUGGER_THREAD);
}
/**
* ide_debugger_get_display_name:
* @self: a #IdeDebugger
*
* Gets the display name for the debugger as the user should see it in various
* UI components.
*
* Returns: The display name for the debugger
*
* Since: 3.32
*/
const gchar *
ide_debugger_get_display_name (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
return priv->display_name;
}
/**
* ide_debugger_set_display_name:
* @self: a #IdeDebugger
*
* Sets the #IdeDebugger:display-name property.
*
* Since: 3.32
*/
void
ide_debugger_set_display_name (IdeDebugger *self,
const gchar *display_name)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_if_fail (IDE_IS_DEBUGGER (self));
if (g_strcmp0 (priv->display_name, display_name) != 0)
{
g_free (priv->display_name);
priv->display_name = g_strdup (display_name);
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DISPLAY_NAME]);
}
}
/**
* ide_debugger_get_can_move:
* @self: a #IdeDebugger
* @movement: the movement to check
*
* Checks to see if the debugger can make the movement matching @movement.
*
* Returns: %TRUE if @movement can be performed.
*
* Since: 3.32
*/
gboolean
ide_debugger_get_can_move (IdeDebugger *self,
IdeDebuggerMovement movement)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
if (IDE_DEBUGGER_GET_CLASS (self)->get_can_move)
return IDE_DEBUGGER_GET_CLASS (self)->get_can_move (self, movement);
return FALSE;
}
/**
* ide_debugger_move_async:
* @self: a #IdeDebugger
* @movement: An #IdeDebuggerMovement
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: (scope async) (closure user_data): A callback to call upon
* completion of the operation.
* @user_data: user data for @callback
*
* Advances the debugger to the next breakpoint or until the debugger stops.
* @movement should describe the type of movement to perform.
*
* Since: 3.32
*/
void
ide_debugger_move_async (IdeDebugger *self,
IdeDebuggerMovement movement,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_MOVEMENT (movement));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->move_async (self, movement, cancellable, callback, user_data);
}
/**
* ide_debugger_move_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult provided to the callback
* @error: A location for a #GError, or %NULL
*
* Notifies that the movement request has been submitted to the debugger.
*
* Note that this does not indicate that the movement has completed successfully,
* only that the command has be submitted.
*
* Returns: %TRUE if successful, otherwise %FALSE
*
* Since: 3.32
*/
gboolean
ide_debugger_move_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->move_finish (self, result, error);
}
/**
* ide_debugger_emit_log:
* @self: a #IdeDebugger
*
* Emits the "log" signal.
*
* Debugger implementations should use this to notify any listeners
* that incoming log information has been recieved.
*
* Use the #IdeDebuggerStream to denote the particular stream.
*
* Since: 3.32
*/
void
ide_debugger_emit_log (IdeDebugger *self,
IdeDebuggerStream stream,
GBytes *content)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_STREAM (stream));
g_return_if_fail (content != NULL);
g_signal_emit (self, signals [LOG], 0, stream, content);
}
/**
* ide_debugger_thread_group_added:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* Debugger implementations should call this to notify that a thread group has
* been added to the inferior.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_group_added (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_signal_emit (self, signals [THREAD_GROUP_ADDED], 0, thread_group);
}
/**
* ide_debugger_thread_group_removed:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* Debugger implementations should call this to notify that a thread group has
* been removed from the inferior.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_group_removed (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_signal_emit (self, signals [THREAD_GROUP_REMOVED], 0, thread_group);
}
/**
* ide_debugger_thread_group_started:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* Debugger implementations should call this to notify that a thread group has
* started executing.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_group_started (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_signal_emit (self, signals [THREAD_GROUP_STARTED], 0, thread_group);
}
/**
* ide_debugger_thread_group_exited:
* @self: an #IdeDebugger
* @thread_group: an #IdeDebuggerThreadGroup
*
* Debugger implementations should call this to notify that a thread group has
* exited.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_group_exited (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_signal_emit (self, signals [THREAD_GROUP_EXITED], 0, thread_group);
}
/**
* ide_debugger_emit_thread_added:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* Emits the #IdeDebugger::thread-added signal notifying that a new thread
* has been added to the inferior.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_added (IdeDebugger *self,
IdeDebuggerThread *thread)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_signal_emit (self, signals [THREAD_ADDED], 0, thread);
}
/**
* ide_debugger_emit_thread_removed:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* Emits the #IdeDebugger::thread-removed signal notifying that a thread has
* been removed to the inferior.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_removed (IdeDebugger *self,
IdeDebuggerThread *thread)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_signal_emit (self, signals [THREAD_REMOVED], 0, thread);
}
/**
* ide_debugger_emit_thread_selected:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
*
* Emits the #IdeDebugger::thread-selected signal notifying that a thread
* has been set as the current debugging thread.
*
* Since: 3.32
*/
void
ide_debugger_emit_thread_selected (IdeDebugger *self,
IdeDebuggerThread *thread)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_signal_emit (self, signals [THREAD_SELECTED], 0, thread);
}
/**
* ide_debugger_emit_breakpoint_added:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* Emits the #IdeDebugger::breakpoint-added signal.
*
* Debugger implementations should call this when a new breakpoint
* has been registered with the debugger.
*
* If a breakpoint has changed, you should use
* ide_debugger_emit_breakpoint_modified() to notify of the modification.
*
* Since: 3.32
*/
void
ide_debugger_emit_breakpoint_added (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_signal_emit (self, signals [BREAKPOINT_ADDED], 0, breakpoint);
}
/**
* ide_debugger_emit_breakpoint_removed:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* Emits the #IdeDebugger::breakpoint-removed signal.
*
* Debugger implementations should call this when a breakpoint has been removed
* either manually or automatically by the debugger.
*
* If a breakpoint has changed, you should use
* ide_debugger_emit_breakpoint_modified() to notify of the modification.
*
* Since: 3.32
*/
void
ide_debugger_emit_breakpoint_removed (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_signal_emit (self, signals [BREAKPOINT_REMOVED], 0, breakpoint);
}
/**
* ide_debugger_emit_breakpoint_modified:
* @self: an #IdeDebugger
* @breakpoint: an #IdeDebuggerBreakpoint
*
* Emits the #IdeDebugger::breakpoint-modified signal.
*
* Debugger implementations should call this when a breakpoint has changed
* in the underlying debugger.
*
* Since: 3.32
*/
void
ide_debugger_emit_breakpoint_modified (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_signal_emit (self, signals [BREAKPOINT_MODIFIED], 0, breakpoint);
}
/**
* ide_debugger_emit_running:
* @self: an #IdeDebugger
*
* Emits the "running" signal.
*
* Debugger implementations should call this when the debugger has started
* or restarted executing the inferior.
*
* Since: 3.32
*/
void
ide_debugger_emit_running (IdeDebugger *self)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_signal_emit (self, signals [RUNNING], 0);
}
/**
* ide_debugger_emit_stopped:
* @self: an #IdeDebugger
* @stop_reason: the reason the debugger stopped
* @breakpoint: the breakpoint representing the stop location
*
* Emits the "stopped" signal.
*
* Debugger implementations should call this when the debugger has stopped
* and include the reason and location of the stop.
*
* Since: 3.32
*/
void
ide_debugger_emit_stopped (IdeDebugger *self,
IdeDebuggerStopReason stop_reason,
IdeDebuggerBreakpoint *breakpoint)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_STOP_REASON (stop_reason));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_signal_emit (self, signals [STOPPED], 0, stop_reason, breakpoint);
}
/**
* ide_debugger_emit_library_loaded:
* @self: an #IdeDebugger
* @library: an #IdeDebuggerLibrary
*
* Emits the "library-loaded" signal.
*
* Debugger implementations should call this when the debugger has loaded
* a new library.
*
* Since: 3.32
*/
void
ide_debugger_emit_library_loaded (IdeDebugger *self,
IdeDebuggerLibrary *library)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_LIBRARY (library));
g_signal_emit (self, signals [LIBRARY_LOADED], 0, library);
}
/**
* ide_debugger_emit_library_unloaded:
* @self: an #IdeDebugger
* @library: an #IdeDebuggerLibrary
*
* Emits the "library-unloaded" signal.
*
* Debugger implementations should call this when the debugger has unloaded a
* library.
*
* Since: 3.32
*/
void
ide_debugger_emit_library_unloaded (IdeDebugger *self,
IdeDebuggerLibrary *library)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_LIBRARY (library));
g_signal_emit (self, signals [LIBRARY_UNLOADED], 0, library);
}
/**
* ide_debugger_list_breakpoints_async:
* @self: An #IdeDebugger
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a callback to call upon completion
* @user_data: user data for @callback
*
* Asynchronously requests the list of current breakpoints from the debugger.
*
* #IdeDebugger implementations must implement the virtual function
* for this method.
*
* Since: 3.32
*/
void
ide_debugger_list_breakpoints_async (IdeDebugger *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->list_breakpoints_async (self, cancellable, callback, user_data);
}
/**
* ide_debugger_list_breakpoints_finish:
* @self: An #IdeDebugger
* @result: a #GAsyncResult provided to the async callback
* @error: a location for a #GError or %NULL
*
* Gets the list of breakpoints from the debugger.
*
* Returns: (transfer full) (element-type Ide.DebuggerBreakpoint): a #GPtrArray
* of breakpoints that are registered with the debugger.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_list_breakpoints_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->list_breakpoints_finish (self, result, error);
}
/**
* ide_debugger_insert_breakpoint_async:
* @self: An #IdeDebugger
* @breakpoint: An #IdeDebuggerBreakpoint
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: an async callback to complete the operation
* @user_data: user data for @callback
*
* Asynchronously requests that a breakpoint is added to the debugger.
*
* This asynchronous function may complete before the breakpoint has been
* registered in the debugger. Debugger implementations will emit
* #IdeDebugger::breakpoint-added when a breakpoint has been registered.
*
* Since: 3.32
*/
void
ide_debugger_insert_breakpoint_async (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->insert_breakpoint_async (self,
breakpoint,
cancellable,
callback,
user_data);
}
/**
* ide_debugger_insert_breakpoint_finish:
* @self: An #IdeDebugger
* @result: a #GAsyncResult or %NULL
* @error: a #GError, or %NULL
*
* Completes a request to asynchronously insert a breakpoint.
*
* See also: ide_debugger_insert_breakpoint_async()
*
* Returns: %TRUE if the command was submitted successfully; otherwise %FALSE
* and @error is set.
*
* Since: 3.32
*/
gboolean
ide_debugger_insert_breakpoint_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->insert_breakpoint_finish (self, result, error);
}
/**
* ide_debugger_remove_breakpoint_async:
* @self: An #IdeDebugger
* @breakpoint: An #IdeDebuggerBreakpoint
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: an async callback to complete the operation
* @user_data: user data for @callback
*
* Asynchronously requests that a breakpoint is removed from the debugger.
*
* This asynchronous function may complete before the breakpoint has been
* removed by the debugger. Debugger implementations will emit
* #IdeDebugger::breakpoint-removed when a breakpoint has been removed.
*
* Since: 3.32
*/
void
ide_debugger_remove_breakpoint_async (IdeDebugger *self,
IdeDebuggerBreakpoint *breakpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->remove_breakpoint_async (self,
breakpoint,
cancellable,
callback,
user_data);
}
/**
* ide_debugger_remove_breakpoint_finish:
* @self: An #IdeDebugger
* @result: a #GAsyncResult or %NULL
* @error: a #GError, or %NULL
*
* Completes a request to asynchronously remove a breakpoint.
*
* See also: ide_debugger_remove_breakpoint_async()
*
* Returns: %TRUE if the command was submitted successfully; otherwise %FALSE and @error is set.
*
* Since: 3.32
*/
gboolean
ide_debugger_remove_breakpoint_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->remove_breakpoint_finish (self, result, error);
}
/**
* ide_debugger_modify_breakpoint_async:
* @self: An #IdeDebugger
* @change: An #IdeDebuggerBreakpointChange
* @breakpoint: An #IdeDebuggerBreakpoint
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: an async callback to complete the operation
* @user_data: user data for @callback
*
* Asynchronously requests that a breakpoint is modified by the debugger backend.
*
* Specify @change for how to modify the breakpoint.
*
* This asynchronous function may complete before the breakpoint has been
* modified by the debugger. Debugger implementations will emit
* #IdeDebugger::breakpoint-modified when a breakpoint has been removed.
*
* Since: 3.32
*/
void
ide_debugger_modify_breakpoint_async (IdeDebugger *self,
IdeDebuggerBreakpointChange change,
IdeDebuggerBreakpoint *breakpoint,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT_CHANGE (change));
g_return_if_fail (IDE_IS_DEBUGGER_BREAKPOINT (breakpoint));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->modify_breakpoint_async (self,
change,
breakpoint,
cancellable,
callback,
user_data);
}
/**
* ide_debugger_modify_breakpoint_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult
* @error: a location for a #GError or %NULL
*
* Completes an asynchronous request to modify a breakpoint.
*
* Note that this only completes the submission of the request, if you need to
* know when the breakpoint has been modified, listen to the
* #IdeDebugger::breakpoint-modified signal.
*
* Returns: %TRUE if successful; otherwise %FALSE and @error is set.
*
* Since: 3.32
*/
gboolean
ide_debugger_modify_breakpoint_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->modify_breakpoint_finish (self, result, error);
}
/**
* ide_debugger_get_breakpoints:
* @self: An #IdeDebugger
*
* Gets the breakpoints for the #IdeDebugger.
*
* Contrast this to ide_debugger_list_breakpoints_async() which will query
* the debugger backend for breakpoints. This #GListModel containing
* #IdeDebuggerBreakpoint instances is updated as necessary by listening
* to various breakpoint related signals on the #IdeDebugger instance.
*
* This is primarily out of convenience to be used by UI which wants to
* display information on breakpoints.
*
* Returns: (transfer none) (not nullable): a #GListModel of #IdeDebuggerBreakpoint
*
* Since: 3.32
*/
GListModel *
ide_debugger_get_breakpoints (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
return G_LIST_MODEL (priv->breakpoints);
}
/**
* ide_debugger_get_thread_groups:
* @self: a #IdeDebugger
*
* Gets the thread groups that have been registered by the debugger.
*
* The resulting #GListModel accuracy is based on the #IdeDebugger
* implementation emitting varous thread-group modification signals correctly.
*
* Returns: (transfer none) (not nullable): a #GListModel of #IdeDebuggerThreadGroup
*
* Since: 3.32
*/
GListModel *
ide_debugger_get_thread_groups (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
return G_LIST_MODEL (priv->thread_groups);
}
/**
* ide_debugger_get_threads:
* @self: a #IdeDebugger
*
* Gets the threads that have been registered by the debugger.
*
* The resulting #GListModel accuracy is based on the #IdeDebugger
* implementation emitting varous thread modification signals correctly.
*
* Returns: (transfer none) (not nullable): a #GListModel of #IdeDebuggerThread
*
* Since: 3.32
*/
GListModel *
ide_debugger_get_threads (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
return G_LIST_MODEL (priv->threads);
}
void
ide_debugger_list_frames_async (IdeDebugger *self,
IdeDebuggerThread *thread,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->list_frames_async (self, thread, cancellable, callback, user_data);
}
/**
* ide_debugger_list_frames_finish:
*
*
*
* Returns: (transfer full) (element-type Ide.DebuggerFrame) (nullable): An
* array of debugger frames or %NULL and @error is set.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_list_frames_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->list_frames_finish (self, result, error);
}
/**
* ide_debugger_get_selected_thread:
* @self: An #IdeDebugger
*
* Gets the current selected thread by the debugger.
*
* Returns: (transfer none) (nullable): An #IdeDebuggerThread or %NULL
*
* Since: 3.32
*/
IdeDebuggerThread *
ide_debugger_get_selected_thread (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
return priv->selected;
}
/**
* ide_debugger_interrupt_async:
* @self: a #IdeDebugger
* @thread_group: (nullable): An #IdeDebuggerThreadGroup
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: (closure user_data): a callback to execute upon completion
* @user_data: closure data for @callback
*
* Asynchronously requests that the debugger interrupts execution of a thread
* group. Thread groups are a collection of threads that are executed or
* stopped together and on gdb on Linux, this is the default for all threads in
* the process.
*
* Since: 3.32
*/
void
ide_debugger_interrupt_async (IdeDebugger *self,
IdeDebuggerThreadGroup *thread_group,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (!thread_group || IDE_IS_DEBUGGER_THREAD_GROUP (thread_group));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->interrupt_async (self, thread_group, cancellable, callback, user_data);
}
gboolean
ide_debugger_interrupt_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->interrupt_finish (self, result, error);
}
gboolean
ide_debugger_get_is_running (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
return priv->is_running;
}
gboolean
_ide_debugger_get_has_started (IdeDebugger *self)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
return priv->has_started;
}
void
ide_debugger_send_signal_async (IdeDebugger *self,
gint signum,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->send_signal_async (self, signum, cancellable, callback, user_data);
}
gboolean
ide_debugger_send_signal_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->send_signal_finish (self, result, error);
}
/**
* ide_debugger_locate_binary_at_address:
* @self: a #IdeDebugger
* @address: the address within the inferior process space
*
* Attempts to locate the binary that contains a given address.
*
* @address should be an address within the inferiors process space.
*
* This works by keeping track of libraries as they are loaded and unloaded and
* their associated file mappings.
*
* Currently, the filename will match the name in the inferior mount namespace,
* but that may change based on future design changes.
*
* Returns: the filename of the binary or %NULL
*
* Since: 3.32
*/
const gchar *
ide_debugger_locate_binary_at_address (IdeDebugger *self,
IdeDebuggerAddress address)
{
IdeDebuggerPrivate *priv = ide_debugger_get_instance_private (self);
const IdeDebuggerAddressMapEntry *entry;
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
entry = ide_debugger_address_map_lookup (priv->map, address);
if (entry != NULL)
return entry->filename;
return NULL;
}
/**
* ide_debugger_list_locals_async:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
* @frame: an #IdeDebuggerFrame
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: A callback to call once the operation has finished
* @user_data: user data for @callback
*
* Requests the debugger backend to list the locals that are available to the
* given @frame of @thread.
*
* Since: 3.32
*/
void
ide_debugger_list_locals_async (IdeDebugger *self,
IdeDebuggerThread *thread,
IdeDebuggerFrame *frame,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_return_if_fail (IDE_IS_DEBUGGER_FRAME (frame));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->list_locals_async (self,
thread,
frame,
cancellable,
callback,
user_data);
}
/**
* ide_debugger_list_locals_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult
* @error: a location for a #GError or %NULL
*
* Completes an asynchronous request to ide_debugger_list_locals_async().
*
* Returns: (transfer full) (element-type Ide.DebuggerVariable): a #GPtrArray of
* #IdeDebuggerVariable if successful; otherwise %NULL and error is set.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_list_locals_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->list_locals_finish (self, result, error);
}
/**
* ide_debugger_list_params_async:
* @self: an #IdeDebugger
* @thread: an #IdeDebuggerThread
* @frame: an #IdeDebuggerFrame
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: A callback to call once the operation has finished
* @user_data: user data for @callback
*
* Requests the debugger backend to list the parameters to the given stack
* frame.
*
* Since: 3.32
*/
void
ide_debugger_list_params_async (IdeDebugger *self,
IdeDebuggerThread *thread,
IdeDebuggerFrame *frame,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_DEBUGGER_THREAD (thread));
g_return_if_fail (IDE_IS_DEBUGGER_FRAME (frame));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->list_params_async (self,
thread,
frame,
cancellable,
callback,
user_data);
}
/**
* ide_debugger_list_params_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult
* @error: a location for a #GError or %NULL
*
* Completes an asynchronous request to ide_debugger_list_params_async().
*
* Returns: (transfer full) (element-type Ide.DebuggerVariable): a #GPtrArray of
* #IdeDebuggerVariable if successful; otherwise %NULL and error is set.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_list_params_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->list_params_finish (self, result, error);
}
/**
* ide_debugger_list_registers_async:
* @self: an #IdeDebugger
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: A callback to call once the operation has finished
* @user_data: user data for @callback
*
* Requests the list of registers and their values.
*
* Since: 3.32
*/
void
ide_debugger_list_registers_async (IdeDebugger *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->list_registers_async (self, cancellable, callback, user_data);
}
/**
* ide_debugger_list_registers_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult
* @error: a location for a #GError or %NULL
*
* Completes an asynchronous request to ide_debugger_list_registers_async().
*
* Returns: (transfer full) (element-type Ide.DebuggerRegister): a #GPtrArray of
* #IdeDebuggerRegister if successful; otherwise %NULL and error is set.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_list_registers_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->list_registers_finish (self, result, error);
}
/**
* ide_debugger_disassemble_async:
* @self: an #IdeDebugger
* @range: an #IdeDebuggerAddressRange to disassemble
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: A callback to call once the operation has finished
* @user_data: user data for @callback
*
* Disassembles the address range requested.
*
* Since: 3.32
*/
void
ide_debugger_disassemble_async (IdeDebugger *self,
const IdeDebuggerAddressRange *range,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (range != NULL);
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
IDE_DEBUGGER_GET_CLASS (self)->disassemble_async (self, range, cancellable, callback, user_data);
}
/**
* ide_debugger_disassemble_finish:
* @self: a #IdeDebugger
* @result: a #GAsyncResult
* @error: a location for a #GError or %NULL
*
* Completes an asynchronous request to ide_debugger_disassemble_async().
*
* Returns: (transfer full) (element-type Ide.DebuggerInstruction): a #GPtrArray
* of #IdeDebuggerInstruction if successful; otherwise %NULL and error is set.
*
* Since: 3.32
*/
GPtrArray *
ide_debugger_disassemble_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
return IDE_DEBUGGER_GET_CLASS (self)->disassemble_finish (self, result, error);
}
/**
* ide_debugger_supports_runner:
* @self: an #IdeDebugger
* @runner: an #IdeRunner
* @priority: (out): A location for a priority
*
* Checks if the debugger supports a given runner. The debugger may need
* to check if the binary type matches it's expectation.
*
* Returns: %TRUE if the #IdeDebugger supports the runner.
*
* Since: 3.32
*/
gboolean
ide_debugger_supports_runner (IdeDebugger *self,
IdeRunner *runner,
gint *priority)
{
gint dummy = 0;
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (IDE_IS_RUNNER (runner), FALSE);
if (priority == NULL)
priority = &dummy;
else
*priority = 0;
return IDE_DEBUGGER_GET_CLASS (self)->supports_runner (self, runner, priority);
}
/**
* ide_debugger_prepare:
* @self: an #IdeDebugger
* @runner: an #IdeRunner
*
* Prepares the runner to launch a debugger and target process.
*
* Since: 3.32
*/
void
ide_debugger_prepare (IdeDebugger *self,
IdeRunner *runner)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (IDE_IS_RUNNER (runner));
if (IDE_DEBUGGER_GET_CLASS (self)->prepare)
IDE_DEBUGGER_GET_CLASS (self)->prepare (self, runner);
}
/**
* ide_debugger_interpret_async:
* @self: an #IdeDebugger
* @command: a command to execute
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: a callback to execute, or %NULL
* @user_data: user data for @callback
*
* Asynchronously requests that the debugger interpret the command.
*
* This is used by the interactive-console to submit commands to the debugger
* that are in the native syntax of that debugger.
*
* The debugger is expected to return any textual output via the
* IdeDebugger::log signal.
*
* Call ide_debugger_interpret_finish() from @callback to determine if the
* command was interpreted.
*
* Since: 3.32
*/
void
ide_debugger_interpret_async (IdeDebugger *self,
const gchar *command,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (IDE_IS_DEBUGGER (self));
g_return_if_fail (command != NULL);
return IDE_DEBUGGER_GET_CLASS (self)->interpret_async (self, command, cancellable, callback, user_data);
}
/**
* ide_debugger_interpret_finish:
* @self: an #IdeDebugger
* @result: a #GAsyncResult provided to callback
* @error: a location for a #GError, or %NULL
*
* Retrieves the result of the asynchronous operation to interpret a debugger
* command.
*
* Returns: %TRUE if the command was interpreted, otherwise %FALSE and
* @error is set.
*
* Since: 3.32
*/
gboolean
ide_debugger_interpret_finish (IdeDebugger *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (IDE_IS_DEBUGGER (self), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
return IDE_DEBUGGER_GET_CLASS (self)->interpret_finish (self, result, error);
}