aboutsummaryrefslogtreecommitdiff
path: root/gdb/guile/scm-breakpoint.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/guile/scm-breakpoint.c')
-rw-r--r--gdb/guile/scm-breakpoint.c1200
1 files changed, 1200 insertions, 0 deletions
diff --git a/gdb/guile/scm-breakpoint.c b/gdb/guile/scm-breakpoint.c
new file mode 100644
index 0000000..d022377
--- /dev/null
+++ b/gdb/guile/scm-breakpoint.c
@@ -0,0 +1,1200 @@
+/* Scheme interface to breakpoints.
+
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+/* See README file in this directory for implementation notes, coding
+ conventions, et.al. */
+
+#include "defs.h"
+#include "value.h"
+#include "exceptions.h"
+#include "breakpoint.h"
+#include "gdbcmd.h"
+#include "gdbthread.h"
+#include "observer.h"
+#include "cli/cli-script.h"
+#include "ada-lang.h"
+#include "arch-utils.h"
+#include "language.h"
+#include "guile-internal.h"
+
+/* The <gdb:breakpoint> smob.
+ N.B.: The name of this struct is known to breakpoint.h. */
+
+typedef struct gdbscm_breakpoint_object
+{
+ /* This always appears first. */
+ gdb_smob base;
+
+ /* The breakpoint number according to gdb.
+ This is recorded here because BP will be NULL when deleted. */
+ int number;
+
+ /* The gdb breakpoint object, or NULL if the breakpoint has been deleted. */
+ struct breakpoint *bp;
+
+ /* Backlink to our containing <gdb:breakpoint> smob.
+ This is needed when we are deleted, we need to unprotect the object
+ from GC. */
+ SCM containing_scm;
+
+ /* A stop condition or #f. */
+ SCM stop;
+} breakpoint_smob;
+
+static const char breakpoint_smob_name[] = "gdb:breakpoint";
+
+/* The tag Guile knows the breakpoint smob by. */
+static scm_t_bits breakpoint_smob_tag;
+
+/* Variables used to pass information between the breakpoint_smob
+ constructor and the breakpoint-created hook function. */
+static SCM pending_breakpoint_scm = SCM_BOOL_F;
+
+/* Keywords used by create-breakpoint!. */
+static SCM type_keyword;
+static SCM wp_class_keyword;
+static SCM internal_keyword;
+
+/* Administrivia for breakpoint smobs. */
+
+/* The smob "mark" function for <gdb:breakpoint>. */
+
+static SCM
+bpscm_mark_breakpoint_smob (SCM self)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+
+ /* We don't mark containing_scm here. It is just a backlink to our
+ container, and is gc'protected until the breakpoint is deleted. */
+
+ scm_gc_mark (bp_smob->stop);
+
+ /* Do this last. */
+ return gdbscm_mark_gsmob (&bp_smob->base);
+}
+
+/* The smob "free" function for <gdb:breakpoint>. */
+
+static size_t
+bpscm_free_breakpoint_smob (SCM self)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+
+ if (bp_smob->bp)
+ bp_smob->bp->scm_bp_object = NULL;
+
+ /* Not necessary, done to catch bugs. */
+ bp_smob->bp = NULL;
+ bp_smob->containing_scm = SCM_UNDEFINED;
+ bp_smob->stop = SCM_UNDEFINED;
+
+ return 0;
+}
+
+/* Return the name of TYPE.
+ This doesn't handle all types, just the ones we export. */
+
+static const char *
+bpscm_type_to_string (enum bptype type)
+{
+ switch (type)
+ {
+ case bp_none: return "BP_NONE";
+ case bp_breakpoint: return "BP_BREAKPOINT";
+ case bp_watchpoint: return "BP_WATCHPOINT";
+ case bp_hardware_watchpoint: return "BP_HARDWARE_WATCHPOINT";
+ case bp_read_watchpoint: return "BP_READ_WATCHPOINT";
+ case bp_access_watchpoint: return "BP_ACCESS_WATCHPOINT";
+ default: return "internal/other";
+ }
+}
+
+/* Return the name of ENABLE_STATE. */
+
+static const char *
+bpscm_enable_state_to_string (enum enable_state enable_state)
+{
+ switch (enable_state)
+ {
+ case bp_disabled: return "disabled";
+ case bp_enabled: return "enabled";
+ case bp_call_disabled: return "call_disabled";
+ case bp_permanent: return "permanent";
+ default: return "unknown";
+ }
+}
+
+/* The smob "print" function for <gdb:breakpoint>. */
+
+static int
+bpscm_print_breakpoint_smob (SCM self, SCM port, scm_print_state *pstate)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (self);
+ struct breakpoint *b = bp_smob->bp;
+
+ gdbscm_printf (port, "#<%s", breakpoint_smob_name);
+
+ /* Only print what we export to the user.
+ The rest are possibly internal implementation details. */
+
+ gdbscm_printf (port, " #%d", bp_smob->number);
+
+ /* Careful, the breakpoint may be invalid. */
+ if (b != NULL)
+ {
+ gdbscm_printf (port, " %s %s %s",
+ bpscm_type_to_string (b->type),
+ bpscm_enable_state_to_string (b->enable_state),
+ b->silent ? "silent" : "noisy");
+
+ gdbscm_printf (port, " hit:%d", b->hit_count);
+ gdbscm_printf (port, " ignore:%d", b->ignore_count);
+
+ if (b->addr_string != NULL)
+ gdbscm_printf (port, " @%s", b->addr_string);
+ }
+
+ scm_puts (">", port);
+
+ scm_remember_upto_here_1 (self);
+
+ /* Non-zero means success. */
+ return 1;
+}
+
+/* Low level routine to create a <gdb:breakpoint> object. */
+
+static SCM
+bpscm_make_breakpoint_smob (void)
+{
+ breakpoint_smob *bp_smob = (breakpoint_smob *)
+ scm_gc_malloc (sizeof (breakpoint_smob), breakpoint_smob_name);
+ SCM bp_scm;
+
+ bp_smob->number = -1;
+ bp_smob->bp = NULL;
+ bp_smob->stop = SCM_BOOL_F;
+ bp_scm = scm_new_smob (breakpoint_smob_tag, (scm_t_bits) bp_smob);
+ bp_smob->containing_scm = bp_scm;
+ gdbscm_init_gsmob (&bp_smob->base);
+
+ return bp_scm;
+}
+
+/* Return non-zero if we want a Scheme wrapper for breakpoint B.
+ If FROM_SCHEME is non-zero,this is called for a breakpoint created
+ by the user from Scheme. Otherwise it is zero. */
+
+static int
+bpscm_want_scm_wrapper_p (struct breakpoint *bp, int from_scheme)
+{
+ /* Don't create <gdb:breakpoint> objects for internal GDB breakpoints. */
+ if (bp->number < 0 && !from_scheme)
+ return 0;
+
+ /* The others are not supported. */
+ if (bp->type != bp_breakpoint
+ && bp->type != bp_watchpoint
+ && bp->type != bp_hardware_watchpoint
+ && bp->type != bp_read_watchpoint
+ && bp->type != bp_access_watchpoint)
+ return 0;
+
+ return 1;
+}
+
+/* Install the Scheme side of a breakpoint, CONTAINING_SCM, in
+ the gdb side BP. */
+
+static void
+bpscm_attach_scm_to_breakpoint (struct breakpoint *bp, SCM containing_scm)
+{
+ breakpoint_smob *bp_smob;
+
+ bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (containing_scm);
+ bp_smob->number = bp->number;
+ bp_smob->bp = bp;
+ bp_smob->containing_scm = containing_scm;
+ bp_smob->bp->scm_bp_object = bp_smob;
+
+ /* The owner of this breakpoint is not in GC-controlled memory, so we need
+ to protect it from GC until the breakpoint is deleted. */
+ scm_gc_protect_object (containing_scm);
+}
+
+/* Return non-zero if SCM is a breakpoint smob. */
+
+static int
+bpscm_is_breakpoint (SCM scm)
+{
+ return SCM_SMOB_PREDICATE (breakpoint_smob_tag, scm);
+}
+
+/* (breakpoint? scm) -> boolean */
+
+static SCM
+gdbscm_breakpoint_p (SCM scm)
+{
+ return scm_from_bool (bpscm_is_breakpoint (scm));
+}
+
+/* Returns the <gdb:breakpoint> object in SELF.
+ Throws an exception if SELF is not a <gdb:breakpoint> object. */
+
+static SCM
+bpscm_get_breakpoint_arg_unsafe (SCM self, int arg_pos, const char *func_name)
+{
+ SCM_ASSERT_TYPE (bpscm_is_breakpoint (self), self, arg_pos, func_name,
+ breakpoint_smob_name);
+
+ return self;
+}
+
+/* Returns a pointer to the breakpoint smob of SELF.
+ Throws an exception if SELF is not a <gdb:breakpoint> object. */
+
+static breakpoint_smob *
+bpscm_get_breakpoint_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ SCM bp_scm = bpscm_get_breakpoint_arg_unsafe (self, arg_pos, func_name);
+ breakpoint_smob *bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (bp_scm);
+
+ return bp_smob;
+}
+
+/* Return non-zero if breakpoint BP_SMOB is valid. */
+
+static int
+bpscm_is_valid (breakpoint_smob *bp_smob)
+{
+ return bp_smob->bp != NULL;
+}
+
+/* Returns the breakpoint smob in SELF, verifying it's valid.
+ Throws an exception if SELF is not a <gdb:breakpoint> object,
+ or is invalid. */
+
+static breakpoint_smob *
+bpscm_get_valid_breakpoint_smob_arg_unsafe (SCM self, int arg_pos,
+ const char *func_name)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_breakpoint_smob_arg_unsafe (self, arg_pos, func_name);
+
+ if (!bpscm_is_valid (bp_smob))
+ {
+ gdbscm_invalid_object_error (func_name, arg_pos, self,
+ _("<gdb:breakpoint>"));
+ }
+
+ return bp_smob;
+}
+
+/* Breakpoint methods. */
+
+/* (create-breakpoint! string [#:type integer] [#:wp-class integer]
+ [#:internal boolean) -> <gdb:breakpoint> */
+
+static SCM
+gdbscm_create_breakpoint_x (SCM spec_scm, SCM rest)
+{
+ const SCM keywords[] = {
+ type_keyword, wp_class_keyword, internal_keyword, SCM_BOOL_F
+ };
+ char *spec;
+ int type_arg_pos = -1, access_type_arg_pos = -1, internal_arg_pos = -1;
+ int type = bp_breakpoint;
+ int access_type = hw_write;
+ int internal = 0;
+ SCM result;
+ volatile struct gdb_exception except;
+
+ gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#iit",
+ spec_scm, &spec, rest,
+ &type_arg_pos, &type,
+ &access_type_arg_pos, &access_type,
+ &internal_arg_pos, &internal);
+
+ result = bpscm_make_breakpoint_smob ();
+ pending_breakpoint_scm = result;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ struct cleanup *cleanup = make_cleanup (xfree, spec);
+
+ switch (type)
+ {
+ case bp_breakpoint:
+ {
+ create_breakpoint (get_current_arch (),
+ spec, NULL, -1, NULL,
+ 0,
+ 0, bp_breakpoint,
+ 0,
+ AUTO_BOOLEAN_TRUE,
+ &bkpt_breakpoint_ops,
+ 0, 1, internal, 0);
+ break;
+ }
+ case bp_watchpoint:
+ {
+ if (access_type == hw_write)
+ watch_command_wrapper (spec, 0, internal);
+ else if (access_type == hw_access)
+ awatch_command_wrapper (spec, 0, internal);
+ else if (access_type == hw_read)
+ rwatch_command_wrapper (spec, 0, internal);
+ else
+ error (_("Invalid watchpoint access type"));
+ break;
+ }
+ default:
+ error (_("Invalid breakpoint type"));
+ }
+
+ do_cleanups (cleanup);
+ }
+ /* Ensure this gets reset, even if there's an error. */
+ pending_breakpoint_scm = SCM_BOOL_F;
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return result;
+}
+
+/* (breakpoint-delete! <gdb:breakpoint>) -> unspecified
+ Scheme function which deletes the underlying GDB breakpoint. This
+ triggers the breakpoint_deleted observer which will call
+ gdbscm_breakpoint_deleted; that function cleans up the Scheme sections. */
+
+static SCM
+gdbscm_breakpoint_delete_x (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ delete_breakpoint (bp_smob->bp);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* iterate_over_breakpoints function for gdbscm_breakpoints. */
+
+static int
+bpscm_build_bp_list (struct breakpoint *bp, void *arg)
+{
+ SCM *list = arg;
+ breakpoint_smob *bp_smob = bp->scm_bp_object;
+
+ /* Lazily create wrappers for breakpoints created outside Scheme. */
+
+ if (bp_smob == NULL)
+ {
+ if (bpscm_want_scm_wrapper_p (bp, 0))
+ {
+ SCM bp_scm;
+
+ bp_scm = bpscm_make_breakpoint_smob ();
+ bpscm_attach_scm_to_breakpoint (bp, bp_scm);
+ /* Refetch it. */
+ bp_smob = bp->scm_bp_object;
+ }
+ }
+
+ /* Not all breakpoints will have a companion Scheme object.
+ Only breakpoints that trigger the created_breakpoint observer call,
+ and satisfy certain conditions (see bpscm_want_scm_wrapper_p),
+ get a companion object (this includes Scheme-created breakpoints). */
+
+ if (bp_smob != NULL)
+ *list = scm_cons (bp_smob->containing_scm, *list);
+
+ return 0;
+}
+
+/* (breakpoints) -> list
+ Return a list of all breakpoints. */
+
+static SCM
+gdbscm_breakpoints (void)
+{
+ SCM list = SCM_EOL;
+
+ /* If iterate_over_breakpoints returns non-NULL it means the iteration
+ terminated early.
+ In that case abandon building the list and return #f. */
+ if (iterate_over_breakpoints (bpscm_build_bp_list, &list) != NULL)
+ return SCM_BOOL_F;
+
+ return scm_reverse_x (list, SCM_EOL);
+}
+
+/* (breakpoint-valid? <gdb:breakpoint>) -> boolean
+ Returns #t if SELF is still valid. */
+
+static SCM
+gdbscm_breakpoint_valid_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bpscm_is_valid (bp_smob));
+}
+
+/* (breakpoint-enabled? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_enabled_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->enable_state == bp_enabled);
+}
+
+/* (set-breakpoint-enabled? <gdb:breakpoint> boolean) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_enabled_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (gdbscm_is_bool (newvalue), newvalue, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (gdbscm_is_true (newvalue))
+ enable_breakpoint (bp_smob->bp);
+ else
+ disable_breakpoint (bp_smob->bp);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-silent? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_silent_p (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->silent);
+}
+
+/* (set-breakpoint-silent?! <gdb:breakpoint> boolean) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_silent_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (gdbscm_is_bool (newvalue), newvalue, SCM_ARG2, FUNC_NAME,
+ _("boolean"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ breakpoint_set_silent (bp_smob->bp, gdbscm_is_true (newvalue));
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-ignore-count <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_ignore_count (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->ignore_count);
+}
+
+/* (set-breakpoint-ignore-count! <gdb:breakpoint> integer)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_ignore_count_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long value;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX),
+ newvalue, SCM_ARG2, FUNC_NAME, _("integer"));
+
+ value = scm_to_long (newvalue);
+ if (value < 0)
+ value = 0;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ set_ignore_count (bp_smob->number, (int) value, 0);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-hit-count <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_hit_count (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->hit_count);
+}
+
+/* (set-breakpoint-hit-count! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_hit_count_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long value;
+
+ SCM_ASSERT_TYPE (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX),
+ newvalue, SCM_ARG2, FUNC_NAME, _("integer"));
+
+ value = scm_to_long (newvalue);
+ if (value < 0)
+ value = 0;
+
+ if (value != 0)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("hit-count must be zero"));
+ }
+
+ bp_smob->bp->hit_count = 0;
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-thread <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_thread (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ if (bp_smob->bp->thread == -1)
+ return SCM_BOOL_F;
+
+ return scm_from_long (bp_smob->bp->thread);
+}
+
+/* (set-breakpoint-thread! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_thread_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long id;
+
+ if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
+ {
+ id = scm_to_long (newvalue);
+ if (! valid_thread_id (id))
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("invalid thread id"));
+ }
+ }
+ else if (gdbscm_is_false (newvalue))
+ id = -1;
+ else
+ SCM_ASSERT_TYPE (0, newvalue, SCM_ARG2, FUNC_NAME, _("integer or #f"));
+
+ breakpoint_set_thread (bp_smob->bp, id);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-task <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_task (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ if (bp_smob->bp->task == 0)
+ return SCM_BOOL_F;
+
+ return scm_from_long (bp_smob->bp->task);
+}
+
+/* (set-breakpoint-task! <gdb:breakpoint> integer) -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_task_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ long id;
+ int valid_id = 0;
+ volatile struct gdb_exception except;
+
+ if (scm_is_signed_integer (newvalue, LONG_MIN, LONG_MAX))
+ {
+ id = scm_to_long (newvalue);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ valid_id = valid_task_id (id);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ if (! valid_id)
+ {
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG2, newvalue,
+ _("invalid task id"));
+ }
+ }
+ else if (gdbscm_is_false (newvalue))
+ id = 0;
+ else
+ SCM_ASSERT_TYPE (0, newvalue, SCM_ARG2, FUNC_NAME, _("integer or #f"));
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ breakpoint_set_task (bp_smob->bp, id);
+ }
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-location <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_location (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+
+ if (bp_smob->bp->type != bp_breakpoint)
+ return SCM_BOOL_F;
+
+ str = bp_smob->bp->addr_string;
+ if (! str)
+ str = "";
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (breakpoint-expression <gdb:breakpoint>) -> string
+ This is only valid for watchpoints.
+ Returns #f for non-watchpoints. */
+
+static SCM
+gdbscm_breakpoint_expression (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+ struct watchpoint *wp;
+
+ if (!is_watchpoint (bp_smob->bp))
+ return SCM_BOOL_F;
+
+ wp = (struct watchpoint *) bp_smob->bp;
+
+ str = wp->exp_string;
+ if (! str)
+ str = "";
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (breakpoint-condition <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_condition (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *str;
+
+ str = bp_smob->bp->cond_string;
+ if (! str)
+ return SCM_BOOL_F;
+
+ return gdbscm_scm_from_c_string (str);
+}
+
+/* (set-breakpoint-condition! <gdb:breakpoint> string|#f)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_condition_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ char *exp;
+ volatile struct gdb_exception except;
+
+ SCM_ASSERT_TYPE (scm_is_string (newvalue) || gdbscm_is_false (newvalue),
+ newvalue, SCM_ARG2, FUNC_NAME,
+ _("string or #f"));
+
+ if (gdbscm_is_false (newvalue))
+ exp = NULL;
+ else
+ exp = gdbscm_scm_to_c_string (newvalue);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ set_breakpoint_condition (bp_smob->bp, exp ? exp : "", 0);
+ }
+ xfree (exp);
+ GDBSCM_HANDLE_GDB_EXCEPTION (except);
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-stop <gdb:breakpoint>) -> procedure or #f */
+
+static SCM
+gdbscm_breakpoint_stop (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return bp_smob->stop;
+}
+
+/* (set-breakpoint-stop! <gdb:breakpoint> procedure|#f)
+ -> unspecified */
+
+static SCM
+gdbscm_set_breakpoint_stop_x (SCM self, SCM newvalue)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ const struct extension_language_defn *extlang = NULL;
+
+ SCM_ASSERT_TYPE (gdbscm_is_procedure (newvalue)
+ || gdbscm_is_false (newvalue),
+ newvalue, SCM_ARG2, FUNC_NAME,
+ _("procedure or #f"));
+
+ if (bp_smob->bp->cond_string != NULL)
+ extlang = get_ext_lang_defn (EXT_LANG_GDB);
+ if (extlang == NULL)
+ extlang = get_breakpoint_cond_ext_lang (bp_smob->bp, EXT_LANG_GUILE);
+ if (extlang != NULL)
+ {
+ char *error_text
+ = xstrprintf (_("Only one stop condition allowed. There is"
+ " currently a %s stop condition defined for"
+ " this breakpoint."),
+ ext_lang_capitalized_name (extlang));
+
+ scm_dynwind_begin (0);
+ gdbscm_dynwind_xfree (error_text);
+ gdbscm_out_of_range_error (FUNC_NAME, SCM_ARG1, self, error_text);
+ /* The following line, while unnecessary, is present for completeness
+ sake. */
+ scm_dynwind_end ();
+ }
+
+ bp_smob->stop = newvalue;
+
+ return SCM_UNSPECIFIED;
+}
+
+/* (breakpoint-commands <gdb:breakpoint>) -> string */
+
+static SCM
+gdbscm_breakpoint_commands (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+ struct breakpoint *bp;
+ long length;
+ volatile struct gdb_exception except;
+ struct ui_file *string_file;
+ struct cleanup *chain;
+ SCM result;
+ char *cmdstr;
+
+ bp = bp_smob->bp;
+
+ if (bp->commands == NULL)
+ return SCM_BOOL_F;
+
+ string_file = mem_fileopen ();
+ chain = make_cleanup_ui_file_delete (string_file);
+
+ ui_out_redirect (current_uiout, string_file);
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ print_command_lines (current_uiout, breakpoint_commands (bp), 0);
+ }
+ ui_out_redirect (current_uiout, NULL);
+ if (except.reason < 0)
+ {
+ do_cleanups (chain);
+ gdbscm_throw_gdb_exception (except);
+ }
+
+ cmdstr = ui_file_xstrdup (string_file, &length);
+ make_cleanup (xfree, cmdstr);
+ result = gdbscm_scm_from_c_string (cmdstr);
+
+ do_cleanups (chain);
+ return result;
+}
+
+/* (breakpoint-type <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_type (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->bp->type);
+}
+
+/* (breakpoint-visible? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_visible (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_bool (bp_smob->bp->number >= 0);
+}
+
+/* (breakpoint-number <gdb:breakpoint>) -> integer */
+
+static SCM
+gdbscm_breakpoint_number (SCM self)
+{
+ breakpoint_smob *bp_smob
+ = bpscm_get_valid_breakpoint_smob_arg_unsafe (self, SCM_ARG1, FUNC_NAME);
+
+ return scm_from_long (bp_smob->number);
+}
+
+/* Return TRUE if "stop" has been set for this breakpoint.
+
+ This is the extension_language_ops.breakpoint_has_cond "method". */
+
+int
+gdbscm_breakpoint_has_cond (const struct extension_language_defn *extlang,
+ struct breakpoint *b)
+{
+ breakpoint_smob *bp_smob = b->scm_bp_object;
+
+ if (bp_smob == NULL)
+ return 0;
+
+ return gdbscm_is_procedure (bp_smob->stop);
+}
+
+/* Call the "stop" method in the breakpoint class.
+ This must only be called if gdbscm_breakpoint_has_cond returns true.
+ If the stop method returns #t, the inferior will be stopped at the
+ breakpoint. Otherwise the inferior will be allowed to continue
+ (assuming other conditions don't indicate "stop").
+
+ This is the extension_language_ops.breakpoint_cond_says_stop "method". */
+
+enum ext_lang_bp_stop
+gdbscm_breakpoint_cond_says_stop
+ (const struct extension_language_defn *extlang, struct breakpoint *b)
+{
+ breakpoint_smob *bp_smob = b->scm_bp_object;
+ SCM predicate_result;
+ int stop;
+
+ if (bp_smob == NULL)
+ return EXT_LANG_BP_STOP_UNSET;
+ if (!gdbscm_is_procedure (bp_smob->stop))
+ return EXT_LANG_BP_STOP_UNSET;
+
+ stop = 1;
+
+ predicate_result
+ = gdbscm_safe_call_1 (bp_smob->stop, bp_smob->containing_scm, NULL);
+
+ if (gdbscm_is_exception (predicate_result))
+ ; /* Exception already printed. */
+ /* If the "stop" function returns #f that means
+ the Scheme breakpoint wants GDB to continue. */
+ else if (gdbscm_is_false (predicate_result))
+ stop = 0;
+
+ return stop ? EXT_LANG_BP_STOP_YES : EXT_LANG_BP_STOP_NO;
+}
+
+/* Event callback functions. */
+
+/* Callback that is used when a breakpoint is created.
+ For breakpoints created by Scheme, i.e., gdbscm_create_breakpoint_x, finish
+ object creation by connecting the Scheme wrapper to the gdb object.
+ We ignore breakpoints created from gdb or python here, we create the
+ Scheme wrapper for those when there's a need to, e.g.,
+ gdbscm_breakpoints. */
+
+static void
+bpscm_breakpoint_created (struct breakpoint *bp)
+{
+ SCM bp_scm;
+
+ if (gdbscm_is_false (pending_breakpoint_scm))
+ return;
+
+ /* Verify our caller error checked the user's request. */
+ gdb_assert (bpscm_want_scm_wrapper_p (bp, 1));
+
+ bp_scm = pending_breakpoint_scm;
+ pending_breakpoint_scm = SCM_BOOL_F;
+
+ bpscm_attach_scm_to_breakpoint (bp, bp_scm);
+}
+
+/* Callback that is used when a breakpoint is deleted. This will
+ invalidate the corresponding Scheme object. */
+
+static void
+bpscm_breakpoint_deleted (struct breakpoint *b)
+{
+ int num = b->number;
+ struct breakpoint *bp;
+
+ /* TODO: Why the lookup? We have B. */
+
+ bp = get_breakpoint (num);
+ if (bp)
+ {
+ breakpoint_smob *bp_smob = bp->scm_bp_object;
+
+ if (bp_smob)
+ {
+ bp_smob->bp = NULL;
+ scm_gc_unprotect_object (bp_smob->containing_scm);
+ }
+ }
+}
+
+/* Initialize the Scheme breakpoint code. */
+
+static const scheme_integer_constant breakpoint_integer_constants[] =
+{
+ { "BP_NONE", bp_none },
+ { "BP_BREAKPOINT", bp_breakpoint },
+ { "BP_WATCHPOINT", bp_watchpoint },
+ { "BP_HARDWARE_WATCHPOINT", bp_hardware_watchpoint },
+ { "BP_READ_WATCHPOINT", bp_read_watchpoint },
+ { "BP_ACCESS_WATCHPOINT", bp_access_watchpoint },
+
+ { "WP_READ", hw_read },
+ { "WP_WRITE", hw_write },
+ { "WP_ACCESS", hw_access },
+
+ END_INTEGER_CONSTANTS
+};
+
+static const scheme_function breakpoint_functions[] =
+{
+ { "create-breakpoint!", 1, 0, 1, gdbscm_create_breakpoint_x,
+ "\
+Create and install a GDB breakpoint object.\n\
+\n\
+ Arguments:\n\
+ location [#:type <type>] [#:wp-class <wp-class>] [#:internal <bool>]" },
+
+ { "breakpoint-delete!", 1, 0, 0, gdbscm_breakpoint_delete_x,
+ "\
+Delete the breakpoint from GDB." },
+
+ { "breakpoints", 0, 0, 0, gdbscm_breakpoints,
+ "\
+Return a list of all GDB breakpoints.\n\
+\n\
+ Arguments: none" },
+
+ { "breakpoint?", 1, 0, 0, gdbscm_breakpoint_p,
+ "\
+Return #t if the object is a <gdb:breakpoint> object." },
+
+ { "breakpoint-valid?", 1, 0, 0, gdbscm_breakpoint_valid_p,
+ "\
+Return #t if the breakpoint has not been deleted from GDB." },
+
+ { "breakpoint-number", 1, 0, 0, gdbscm_breakpoint_number,
+ "\
+Return the breakpoint's number." },
+
+ { "breakpoint-type", 1, 0, 0, gdbscm_breakpoint_type,
+ "\
+Return the type of the breakpoint." },
+
+ { "breakpoint-visible?", 1, 0, 0, gdbscm_breakpoint_visible,
+ "\
+Return #t if the breakpoint is visible to the user." },
+
+ { "breakpoint-location", 1, 0, 0, gdbscm_breakpoint_location,
+ "\
+Return the location of the breakpoint as specified by the user." },
+
+ { "breakpoint-expression", 1, 0, 0, gdbscm_breakpoint_expression,
+ "\
+Return the expression of the breakpoint as specified by the user.\n\
+Valid for watchpoints only, returns #f for non-watchpoints." },
+
+ { "breakpoint-enabled?", 1, 0, 0, gdbscm_breakpoint_enabled_p,
+ "\
+Return #t if the breakpoint is enabled." },
+
+ { "set-breakpoint-enabled!", 2, 0, 0, gdbscm_set_breakpoint_enabled_x,
+ "\
+Set the breakpoint's enabled state.\n\
+\n\
+ Arguments: <gdb:breakpoint boolean" },
+
+ { "breakpoint-silent?", 1, 0, 0, gdbscm_breakpoint_silent_p,
+ "\
+Return #t if the breakpoint is silent." },
+
+ { "set-breakpoint-silent!", 2, 0, 0, gdbscm_set_breakpoint_silent_x,
+ "\
+Set the breakpoint's silent state.\n\
+\n\
+ Arguments: <gdb:breakpoint> boolean" },
+
+ { "breakpoint-ignore-count", 1, 0, 0, gdbscm_breakpoint_ignore_count,
+ "\
+Return the breakpoint's \"ignore\" count." },
+
+ { "set-breakpoint-ignore-count!", 2, 0, 0,
+ gdbscm_set_breakpoint_ignore_count_x,
+ "\
+Set the breakpoint's \"ignore\" count.\n\
+\n\
+ Arguments: <gdb:breakpoint> count" },
+
+ { "breakpoint-hit-count", 1, 0, 0, gdbscm_breakpoint_hit_count,
+ "\
+Return the breakpoint's \"hit\" count." },
+
+ { "set-breakpoint-hit-count!", 2, 0, 0, gdbscm_set_breakpoint_hit_count_x,
+ "\
+Set the breakpoint's \"hit\" count. The value must be zero.\n\
+\n\
+ Arguments: <gdb:breakpoint> 0" },
+
+ { "breakpoint-thread", 1, 0, 0, gdbscm_breakpoint_thread,
+ "\
+Return the breakpoint's thread id or #f if there isn't one." },
+
+ { "set-breakpoint-thread!", 2, 0, 0, gdbscm_set_breakpoint_thread_x,
+ "\
+Set the thread id for this breakpoint.\n\
+\n\
+ Arguments: <gdb:breakpoint> thread-id" },
+
+ { "breakpoint-task", 1, 0, 0, gdbscm_breakpoint_task,
+ "\
+Return the breakpoint's Ada task-id or #f if there isn't one." },
+
+ { "set-breakpoint-task!", 2, 0, 0, gdbscm_set_breakpoint_task_x,
+ "\
+Set the breakpoint's Ada task-id.\n\
+\n\
+ Arguments: <gdb:breakpoint> task-id" },
+
+ { "breakpoint-condition", 1, 0, 0, gdbscm_breakpoint_condition,
+ "\
+Return the breakpoint's condition as specified by the user.\n\
+Return #f if there isn't one." },
+
+ { "set-breakpoint-condition!", 2, 0, 0, gdbscm_set_breakpoint_condition_x,
+ "\
+Set the breakpoint's condition.\n\
+\n\
+ Arguments: <gdb:breakpoint> condition\n\
+ condition: a string" },
+
+ { "breakpoint-stop", 1, 0, 0, gdbscm_breakpoint_stop,
+ "\
+Return the breakpoint's stop predicate.\n\
+Return #f if there isn't one." },
+
+ { "set-breakpoint-stop!", 2, 0, 0, gdbscm_set_breakpoint_stop_x,
+ "\
+Set the breakpoint's stop predicate.\n\
+\n\
+ Arguments: <gdb:breakpoint> procedure\n\
+ procedure: A procedure of one argument, the breakpoint.\n\
+ Its result is true if program execution should stop." },
+
+ { "breakpoint-commands", 1, 0, 0, gdbscm_breakpoint_commands,
+ "\
+Return the breakpoint's commands." },
+
+ END_FUNCTIONS
+};
+
+void
+gdbscm_initialize_breakpoints (void)
+{
+ breakpoint_smob_tag
+ = gdbscm_make_smob_type (breakpoint_smob_name, sizeof (breakpoint_smob));
+ scm_set_smob_mark (breakpoint_smob_tag, bpscm_mark_breakpoint_smob);
+ scm_set_smob_free (breakpoint_smob_tag, bpscm_free_breakpoint_smob);
+ scm_set_smob_print (breakpoint_smob_tag, bpscm_print_breakpoint_smob);
+
+ observer_attach_breakpoint_created (bpscm_breakpoint_created);
+ observer_attach_breakpoint_deleted (bpscm_breakpoint_deleted);
+
+ gdbscm_define_integer_constants (breakpoint_integer_constants, 1);
+ gdbscm_define_functions (breakpoint_functions, 1);
+
+ type_keyword = scm_from_latin1_keyword ("type");
+ wp_class_keyword = scm_from_latin1_keyword ("wp-class");
+ internal_keyword = scm_from_latin1_keyword ("internal");
+}