/* ide-debugger.c * * Copyright 2017-2019 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * 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); }