Guile: temporary breakpoints
authorGeorge Barrett <bob@bob131.so>
Wed, 9 Jun 2021 13:56:11 +0000 (23:56 +1000)
committerSimon Marchi <simon.marchi@polymtl.ca>
Thu, 29 Jul 2021 00:30:24 +0000 (20:30 -0400)
Adds API to the Guile bindings for creating temporary breakpoints and
querying whether an existing breakpoint object is temporary. This is
effectively a transliteration of the Python implementation.

It's worth noting that the added `is_temporary' flag is ignored in the
watchpoint registration path. This replicates the behaviour of the
Python implementation, but might be a bit surprising for users.

gdb/ChangeLog:

2021-06-09  George Barrett  <bob@bob131.so>

* guile/scm-breakpoint.c (gdbscm_breakpoint_object::spec): Add
is_temporary field.
(temporary_keyword): Add keyword object for make-breakpoint
argument parsing.
(gdbscm_make_breakpoint): Accept #:temporary keyword argument
and store the value in the allocated object's
spec.is_temporary.
(gdbscm_register_breakpoint_x): Pass the breakpoint's
spec.is_temporary value to create_breakpoint.
(gdbscm_breakpoint_temporary): Add breakpoint-temporary?
procedure implementation.
(breakpoint_functions::make-breakpoint): Update documentation
string and fix a typo.
(breakpoint_functions::breakpoint-temporary?): Add
breakpoint-temporary? procedure.
(gdbscm_initialize_breakpoints): Initialise temporary_keyword
variable.
NEWS (Guile API): Mention new temporary breakpoints API.

gdb/doc/ChangeLog:

2021-06-09  George Barrett  <bob@bob131.so>

* guile.texi (Breakpoints In Guile): Update make-breakpoint
documentation to reflect new #:temporary argument.
Add documentation for new breakpoint-temporary? procedure.

gdb/testsuite/ChangeLog:

2021-06-09  George Barrett  <bob@bob131.so>

* gdb.guile/scm-breakpoint.exp: Add additional tests for
temporary breakpoints.

Change-Id: I2de332ee7c256f5591d7141ab3ad50d31b871d17

gdb/NEWS
gdb/doc/guile.texi
gdb/guile/scm-breakpoint.c
gdb/testsuite/gdb.guile/scm-breakpoint.exp

index 062249aea98e6a142a248340c7805ea3a1505f82..51b276c2cc18a0ec473b8e48e0aec610bebec1f7 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -267,6 +267,9 @@ QMemTags
      value-reference-value, value-rvalue-reference-value and
      value-const-value.
 
+  ** Temporary breakpoints can now be created with make-breakpoint and
+     tested for using breakpoint-temporary?.
+
 * Python API
 
   ** Inferior objects now contain a read-only 'connection_num' attribute that
index 36fc9a7af797d73c4f8e8ed9ab706075dac614c3..a2448dd62cbe80021a149b499fcdf84c0f104ad2 100644 (file)
@@ -2965,7 +2965,7 @@ The following breakpoint-related procedures are provided by the
 @code{(gdb)} module:
 
 @c TODO: line length
-@deffn {Scheme Procedure} make-breakpoint location @r{[}#:type type@r{]} @r{[}#:wp-class wp-class@r{]} @r{[}#:internal internal@r{]}
+@deffn {Scheme Procedure} make-breakpoint location @r{[}#:type type@r{]} @r{[}#:wp-class wp-class@r{]} @r{[}#:internal internal@r{]} @r{[}#:temporary temporary@r{]}
 Create a new breakpoint at @var{location}, a string naming the
 location of the breakpoint, or an expression that defines a watchpoint.
 The contents can be any location recognized by the @code{break} command,
@@ -2991,6 +2991,11 @@ registered, nor will it be listed in the output from @code{info breakpoints}
 If an internal flag is not provided, the breakpoint is visible
 (non-internal).
 
+The optional @var{temporary} argument makes the breakpoint a temporary
+breakpoint.  Temporary breakpoints are deleted after they have been hit,
+after which the Guile breakpoint is no longer usable (although it may be
+re-registered with @code{register-breakpoint!}).
+
 When a watchpoint is created, @value{GDBN} will try to create a
 hardware assisted watchpoint.  If successful, the type of the watchpoint
 is changed from @code{BP_WATCHPOINT} to @code{BP_HARDWARE_WATCHPOINT}
@@ -3087,6 +3092,15 @@ Return the breakpoint's number --- the identifier used by
 the user to manipulate the breakpoint.
 @end deffn
 
+@deffn {Scheme Procedure} breakpoint-temporary? breakpoint
+Return @code{#t} if the breakpoint was created as a temporary
+breakpoint.  Temporary breakpoints are automatically deleted after
+they've been hit.  Calling this procedure, and all other procedures
+other than @code{breakpoint-valid?} and @code{register-breakpoint!},
+will result in an error after the breakpoint has been hit (since it has
+been automatically deleted).
+@end deffn
+
 @deffn {Scheme Procedure} breakpoint-type breakpoint
 Return the breakpoint's type --- the identifier used to
 determine the actual breakpoint type or use-case.
index 3f25708afffb3961e95ed638a65931d3099e4125..67484e440f539ca2e304749f711c22574fe939a7 100644 (file)
@@ -69,6 +69,9 @@ typedef struct gdbscm_breakpoint_object
 
     /* Non-zero if the breakpoint is an "internal" breakpoint.  */
     int is_internal;
+
+    /* Non-zero if the breakpoint is temporary.  */
+    int is_temporary;
   } spec;
 
   /* The breakpoint number according to gdb.
@@ -103,6 +106,7 @@ static SCM pending_breakpoint_scm = SCM_BOOL_F;
 static SCM type_keyword;
 static SCM wp_class_keyword;
 static SCM internal_keyword;
+static SCM temporary_keyword;
 \f
 /* Administrivia for breakpoint smobs.  */
 
@@ -332,7 +336,7 @@ bpscm_get_valid_breakpoint_smob_arg_unsafe (SCM self, int arg_pos,
 /* Breakpoint methods.  */
 
 /* (make-breakpoint string [#:type integer] [#:wp-class integer]
-    [#:internal boolean) -> <gdb:breakpoint>
+    [#:internal boolean] [#:temporary boolean]) -> <gdb:breakpoint>
 
    The result is the <gdb:breakpoint> Scheme object.
    The breakpoint is not available to be used yet, however.
@@ -342,22 +346,26 @@ static SCM
 gdbscm_make_breakpoint (SCM location_scm, SCM rest)
 {
   const SCM keywords[] = {
-    type_keyword, wp_class_keyword, internal_keyword, SCM_BOOL_F
+    type_keyword, wp_class_keyword, internal_keyword,
+    temporary_keyword, SCM_BOOL_F
   };
   char *s;
   char *location;
-  int type_arg_pos = -1, access_type_arg_pos = -1, internal_arg_pos = -1;
+  int type_arg_pos = -1, access_type_arg_pos = -1,
+      internal_arg_pos = -1, temporary_arg_pos = -1;
   enum bptype type = bp_breakpoint;
   enum target_hw_bp_type access_type = hw_write;
   int internal = 0;
+  int temporary = 0;
   SCM result;
   breakpoint_smob *bp_smob;
 
-  gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#iit",
+  gdbscm_parse_function_args (FUNC_NAME, SCM_ARG1, keywords, "s#iitt",
                              location_scm, &location, rest,
                              &type_arg_pos, &type,
                              &access_type_arg_pos, &access_type,
-                             &internal_arg_pos, &internal);
+                             &internal_arg_pos, &internal,
+                             &temporary_arg_pos, &temporary);
 
   result = bpscm_make_breakpoint_smob ();
   bp_smob = (breakpoint_smob *) SCM_SMOB_DATA (result);
@@ -412,6 +420,7 @@ gdbscm_make_breakpoint (SCM location_scm, SCM rest)
   bp_smob->spec.type = type;
   bp_smob->spec.access_type = access_type;
   bp_smob->spec.is_internal = internal;
+  bp_smob->spec.is_temporary = temporary;
 
   return result;
 }
@@ -447,6 +456,7 @@ gdbscm_register_breakpoint_x (SCM self)
   try
     {
       int internal = bp_smob->spec.is_internal;
+      int temporary = bp_smob->spec.is_temporary;
 
       switch (bp_smob->spec.type)
        {
@@ -457,7 +467,7 @@ gdbscm_register_breakpoint_x (SCM self)
            create_breakpoint (get_current_arch (),
                               eloc.get (), NULL, -1, NULL, false,
                               0,
-                              0, bp_breakpoint,
+                              temporary, bp_breakpoint,
                               0,
                               AUTO_BOOLEAN_TRUE,
                               ops,
@@ -1040,6 +1050,18 @@ gdbscm_breakpoint_number (SCM self)
 
   return scm_from_long (bp_smob->number);
 }
+
+/* (breakpoint-temporary? <gdb:breakpoint>) -> boolean */
+
+static SCM
+gdbscm_breakpoint_temporary (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->disposition == disp_del
+                       || bp_smob->bp->disposition == disp_del_at_next_stop);
+}
 \f
 /* Return TRUE if "stop" has been set for this breakpoint.
 
@@ -1171,9 +1193,9 @@ static const scheme_function breakpoint_functions[] =
 Create a GDB breakpoint object.\n\
 \n\
   Arguments:\n\
-    location [#:type <type>] [#:wp-class <wp-class>] [#:internal <bool>]\n\
+    location [#:type <type>] [#:wp-class <wp-class>] [#:internal <bool>] [#:temporary <bool>]\n\
   Returns:\n\
-    <gdb:breakpoint object" },
+    <gdb:breakpoint> object" },
 
   { "register-breakpoint!", 1, 0, 0,
     as_a_scm_t_subr (gdbscm_register_breakpoint_x),
@@ -1202,6 +1224,10 @@ Return #t if the breakpoint has not been deleted from GDB." },
     "\
 Return the breakpoint's number." },
 
+  { "breakpoint-temporary?", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_temporary),
+    "\
+Return #t if the breakpoint is a temporary breakpoint." },
+
   { "breakpoint-type", 1, 0, 0, as_a_scm_t_subr (gdbscm_breakpoint_type),
     "\
 Return the type of the breakpoint." },
@@ -1345,4 +1371,5 @@ gdbscm_initialize_breakpoints (void)
   type_keyword = scm_from_latin1_keyword ("type");
   wp_class_keyword = scm_from_latin1_keyword ("wp-class");
   internal_keyword = scm_from_latin1_keyword ("internal");
+  temporary_keyword = scm_from_latin1_keyword ("temporary");
 }
index be898cacaa7916a5920b575c5e5443c0cf951478..c5f19e154d7d6fd2391f668a87e73bd1c047e028 100644 (file)
@@ -485,6 +485,38 @@ proc_with_prefix test_bkpt_registration {} {
        "= #t" "breakpoint valid after re-registration"
 }
 
+proc_with_prefix test_bkpt_temporary { } {
+    global srcfile testfile hex decimal
+
+    # Start with a fresh gdb.
+    clean_restart ${testfile}
+
+    if ![gdb_guile_runto_main] {
+       fail "cannot run to main."
+       return 0
+    }
+    delete_breakpoints
+
+    set ibp_location [gdb_get_line_number "Break at multiply."]
+    gdb_scm_test_silent_cmd "guile (define ibp (make-breakpoint \"$ibp_location\" #:temporary #t))" \
+       "create temporary breakpoint"
+    gdb_scm_test_silent_cmd "guile (register-breakpoint! ibp)" \
+       "register ibp"
+    gdb_test "info breakpoints" \
+       "2.*breakpoint.*del.*scm-breakpoint\.c:$ibp_location.*" \
+       "check info breakpoints shows breakpoint with temporary status"
+    gdb_test "guile (print (breakpoint-location ibp))" "scm-breakpoint\.c:$ibp_location*" \
+       "check temporary breakpoint location"
+    gdb_test "guile (print (breakpoint-temporary? ibp))" "#t" \
+       "check breakpoint temporary status"
+    gdb_continue_to_breakpoint "Break at multiply." \
+       ".*$srcfile:$ibp_location.*"
+    gdb_test "guile (print (breakpoint-temporary? ibp))" "Invalid object: <gdb:breakpoint>.*" \
+       "check temporary breakpoint is deleted after being hit"
+    gdb_test "info breakpoints" "No breakpoints or watchpoints.*" \
+       "check info breakpoints shows temporary breakpoint is deleted"
+}
+
 proc_with_prefix test_bkpt_address {} {
     global decimal srcfile
 
@@ -564,5 +596,6 @@ test_watchpoints
 test_bkpt_internal
 test_bkpt_eval_funcs
 test_bkpt_registration
+test_bkpt_temporary
 test_bkpt_address
 test_bkpt_probe