This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC add "convenience functions" to gdb


For quite a while now I've wanted to be able to make a breakpoint
conditional on the current frame's caller.  I was never able to think
of a way to do this.

And, yesterday I was thinking about the 'eval' patch and trying to
understand how I could use it to compile a '.gdbinit' into an
executable and automatically have gdb eval it.  I thought perhaps I
could make a global variable whose value would be the gdb script --
but I couldn't think of how to detect whether or not the symbol
existed.

So, this evening I spent some time adding a new "convenience function"
facility to gdb.  The idea here is that gdb will provide certain
useful functions which can be called during expression evaluation.
Then problems like the above reduce to adding a new internal function
which user scripts can call to query gdb or the inferior.

I chose the syntax $(function arguments) for this.  It is similar to
convenience variables but, as far as I can tell, not valid right now.
Also it is reminiscent of something sort of similar in bash.

This patch only includes a single function, 'caller-matches'.  You can
use it like:

    break somewhere if $(caller-matches main)

This will stop when in 'somewhere' when the caller's function name is
known and has 'main' as a substring.

Obviously this patch is nowhere near complete.  I didn't write
documentation or tests, there are some FIXMEs, I didn't update any
language other than C, and of course it would be nice to have a few
more convenience functions available.

Before going too far with this, though, I wanted to run it past
everyone to get some feedback.  Is this worth pursuing?  Should I just
be waiting for the python integration?

Tom

ChangeLog:
2008-02-29  Tom Tromey  <tromey@redhat.com>

	* value.h (value_of_internalfunc): Declare.
	* value.c (caller_matches): New function.
	(struct internal_function): Define.
	(all_functions): Define.
	(value_of_internalfunc): New function.
	* parser-defs.h (write_dollar_funcall): Declare.
	* parse.c (write_dollar_funcall): New function.
	(operator_length_standard) <OP_INTERNALFUNC>: New case.
	* expression.h (enum exp_opcode) <OP_INTERNALFUNC>: New constant.
	* expprint.c (print_subexp_standard) <OP_INTERNALFUNC>: New case.
	(op_name_standard) <OP_INTERNALFUNC>: Likewise.
	(dump_subexp_body_standard) <OP_INTERNALFUNC>: Likewise.
	* eval.c (evaluate_subexp_standard) <OP_INTERNALFUNC>: New case.
	* c-exp.y (yylex): Recognize $(...).
	* ax-gdb.c (gen_expr) <OP_INTERNALFUNC>: New case.

Index: ax-gdb.c
===================================================================
RCS file: /cvs/src/src/gdb/ax-gdb.c,v
retrieving revision 1.41
diff -u -r1.41 ax-gdb.c
--- ax-gdb.c	5 Feb 2008 15:54:34 -0000	1.41
+++ ax-gdb.c	1 Mar 2008 05:18:49 -0000
@@ -1620,6 +1620,9 @@
     case OP_INTERNALVAR:
       error (_("GDB agent expressions cannot use convenience variables."));
 
+    case OP_INTERNALFUNC:
+      error (_("GDB agent expressions cannot use convenience functions."));
+
       /* Weirdo operator: see comments for gen_repeat for details.  */
     case BINOP_REPEAT:
       /* Note that gen_repeat handles its own argument evaluation.  */
Index: c-exp.y
===================================================================
RCS file: /cvs/src/src/gdb/c-exp.y,v
retrieving revision 1.42
diff -u -r1.42 c-exp.y
--- c-exp.y	9 Jan 2008 19:27:15 -0000	1.42
+++ c-exp.y	1 Mar 2008 05:18:49 -0000
@@ -1636,6 +1636,31 @@
     /* We must have come across a bad character (e.g. ';').  */
     error ("Invalid character '%c' in expression.", c);
 
+  if (c == '$' && tokstart[1] == '(')
+    {
+      /* Convenience function call.  */
+      int cparen_depth = 0;
+      int i;
+      for (i = 1; tokstart[i]; ++i)
+	{
+	  if (tokstart[i] == '(')
+	    ++cparen_depth;
+	  else if (tokstart[i] == ')')
+	    {
+	      if (--cparen_depth == 0)
+		break;
+	    }
+	}
+      if (cparen_depth != 0)
+	error ("Unmatched parentheses in convenience function invocation.");
+      yylval.sval.ptr = &tokstart[2];
+      yylval.sval.length = i - 2;
+      write_dollar_funcall (yylval.sval);
+      lexptr = &tokstart[i + 1];
+      /* FIXME: not exactly right.  */
+      return VARIABLE;
+    }
+
   /* It's a name.  See how long it is.  */
   namelen = 0;
   for (c = tokstart[namelen];
Index: eval.c
===================================================================
RCS file: /cvs/src/src/gdb/eval.c,v
retrieving revision 1.80
diff -u -r1.80 eval.c
--- eval.c	4 Feb 2008 00:23:04 -0000	1.80
+++ eval.c	1 Mar 2008 05:18:49 -0000
@@ -538,6 +538,12 @@
       (*pos) += 2;
       return value_of_internalvar (exp->elts[pc + 1].internalvar);
 
+    case OP_INTERNALFUNC:
+      tem = longest_to_int (exp->elts[pc + 1].longconst);
+      (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
+      /* FIXME: handling noside?  */
+      return value_of_internalfunc (&exp->elts[pc + 2].string, tem);
+
     case OP_STRING:
       tem = longest_to_int (exp->elts[pc + 1].longconst);
       (*pos) += 3 + BYTES_TO_EXP_ELEM (tem + 1);
Index: expprint.c
===================================================================
RCS file: /cvs/src/src/gdb/expprint.c,v
retrieving revision 1.31
diff -u -r1.31 expprint.c
--- expprint.c	1 Jan 2008 22:53:09 -0000	1.31
+++ expprint.c	1 Mar 2008 05:18:50 -0000
@@ -148,6 +148,14 @@
 			internalvar_name (exp->elts[pc + 1].internalvar));
       return;
 
+    case OP_INTERNALFUNC:
+      {
+	const char *name = &exp->elts[pc + 2].string;
+	(*pos) += 3 + BYTES_TO_EXP_ELEM (exp->elts[pc + 1].longconst + 1);
+	fprintf_filtered (stream, "$(%s)", name);
+	return;
+      }
+
     case OP_FUNCALL:
       (*pos) += 2;
       nargs = longest_to_int (exp->elts[pc + 1].longconst);
@@ -692,6 +700,8 @@
       return "OP_REGISTER";
     case OP_INTERNALVAR:
       return "OP_INTERNALVAR";
+    case OP_INTERNALFUNC:
+      return "OP_INTERNALFUNC";
     case OP_FUNCALL:
       return "OP_FUNCALL";
     case OP_STRING:
@@ -973,6 +983,11 @@
 			exp->elts[elt].internalvar->name);
       elt += 2;
       break;
+    case OP_INTERNALFUNC:
+      fprintf_filtered (stream, "Internal function call $(%s)",
+			&exp->elts[elt + 1].string);
+      elt += 3 + BYTES_TO_EXP_ELEM (exp->elts[elt].longconst + 1);
+      break;
     case OP_FUNCALL:
       {
 	int i, nargs;
Index: expression.h
===================================================================
RCS file: /cvs/src/src/gdb/expression.h,v
retrieving revision 1.26
diff -u -r1.26 expression.h
--- expression.h	1 Jan 2008 22:53:09 -0000	1.26
+++ expression.h	1 Mar 2008 05:18:50 -0000
@@ -172,6 +172,12 @@
        With another OP_INTERNALVAR at the end, this makes three exp_elements.  */
     OP_INTERNALVAR,
 
+    /* OP_INTERNALFUNC represents a call to a gdb-internal
+       "convenience function".  Its format is the same as that of a
+       STRUCTOP, but the string data is evaluated as a call to a
+       gdb-provided function.  */
+    OP_INTERNALFUNC,
+
     /* OP_FUNCALL is followed by an integer in the next exp_element.
        The integer is the number of args to the function call.
        That many plus one values from following subexpressions
Index: parse.c
===================================================================
RCS file: /cvs/src/src/gdb/parse.c,v
retrieving revision 1.69
diff -u -r1.69 parse.c
--- parse.c	1 Jan 2008 22:53:12 -0000	1.69
+++ parse.c	1 Mar 2008 05:18:50 -0000
@@ -295,7 +295,7 @@
    strings with embedded null bytes, as is required for some languages.
 
    Don't be fooled by the fact that the string is null byte terminated,
-   this is strictly for the convenience of debugging gdb itself.  Gdb
+   this is strictly for the convenience of debugging gdb itself.
    Gdb does not depend up the string being null terminated, since the
    actual length is recorded in expression elements at each end of the
    string.  The null byte is taken into consideration when computing how
@@ -574,6 +574,14 @@
   return;
 }
 
+void
+write_dollar_funcall (struct stoken str)
+{
+  write_exp_elt_opcode (OP_INTERNALFUNC);
+  write_exp_string (str);
+  write_exp_elt_opcode (OP_INTERNALFUNC);
+}
+
 
 char *
 find_template_name_end (char *p)
@@ -798,6 +806,7 @@
     case OP_OBJC_NSSTRING:	/* Objective C Foundation Class NSString constant */
     case OP_OBJC_SELECTOR:	/* Objective C "@selector" pseudo-op */
     case OP_NAME:
+    case OP_INTERNALFUNC:
       oplen = longest_to_int (expr->elts[endpos - 2].longconst);
       oplen = 4 + BYTES_TO_EXP_ELEM (oplen + 1);
       break;
Index: parser-defs.h
===================================================================
RCS file: /cvs/src/src/gdb/parser-defs.h,v
retrieving revision 1.26
diff -u -r1.26 parser-defs.h
--- parser-defs.h	1 Jan 2008 22:53:12 -0000	1.26
+++ parser-defs.h	1 Mar 2008 05:18:50 -0000
@@ -136,6 +136,8 @@
 extern void write_exp_msymbol (struct minimal_symbol *,
 			       struct type *, struct type *);
 
+extern void write_dollar_funcall (struct stoken str);
+
 extern void write_dollar_variable (struct stoken str);
 
 extern char *find_template_name_end (char *);
Index: value.c
===================================================================
RCS file: /cvs/src/src/gdb/value.c,v
retrieving revision 1.57
diff -u -r1.57 value.c
--- value.c	18 Jan 2008 17:07:40 -0000	1.57
+++ value.c	1 Mar 2008 05:18:50 -0000
@@ -36,6 +36,8 @@
 #include "block.h"
 #include "dfp.h"
 
+#include <ctype.h>
+
 /* Prototypes for exported functions. */
 
 void _initialize_values (void);
@@ -699,6 +701,87 @@
     }
 }
 
+/* Internal functions.  */
+
+/* A builtin function that checks to see if the current frame's
+   caller's function name matches STR.  FIXME: use a regex?  */
+
+static struct value *
+caller_matches (char *str)
+{
+  struct frame_info *frame = get_current_frame ();
+  int result = 0;
+
+  if (frame)
+    frame = get_prev_frame (frame);
+  if (frame)
+    {
+      struct partial_symtab *ps;
+      struct symbol *func;
+      ps = find_pc_psymtab (get_frame_address_in_block (frame));
+      if (ps)
+	PSYMTAB_TO_SYMTAB (ps);
+      /* FIXME see evil magic in print_frame.  */
+      func = find_pc_function (get_frame_address_in_block (frame));
+      if (func)
+	{
+	  /* FIXME: demangle name here?  */
+	  if (strstr (DEPRECATED_SYMBOL_NAME (func), str))
+	    result = 1;
+	}
+    }
+
+  return value_from_longest (builtin_type_int32, result);
+}
+
+/* Represents a single internal function.  */
+struct internal_function
+{
+  /* Name of the function.  */
+  const char *name;
+  /* The function to call.  */
+  struct value *(*impl) (char *);
+};
+
+/* All internal functions, terminated by an entry with NAME == NULL.  */
+static struct internal_function all_functions[] =
+{
+  { "caller-matches", caller_matches },
+  { NULL, NULL }
+};
+
+
+/* Call an internal function and return the result.  EXPR is the
+   command-line, including the function name.  LENGTH is the length of
+   the command string.  */
+
+struct value *
+value_of_internalfunc (char *expr, int length)
+{
+  int cmdlen, argstart, i;
+  for (cmdlen = 0; expr[cmdlen] && ! isspace (expr[cmdlen]); ++cmdlen)
+    ;
+  for (argstart = cmdlen;
+       expr[argstart] && isspace (expr[argstart]);
+       ++argstart)
+    ;
+
+  for (i = 0; all_functions[i].name; ++i)
+    {
+      if (! strncmp (expr, all_functions[i].name, cmdlen))
+	{
+	  char *arg = xstrndup (expr + argstart, length - argstart);
+	  struct value *result = all_functions[i].impl (arg);
+	  free (arg);
+	  return result;
+	}
+    }
+
+  /* FIXME: bogus name here.  */
+  error ("No built-in function named %s", expr);
+}
+
+
 /* Internal variables.  These are variables within the debugger
    that hold values assigned by debugger commands.
    The user refers to them with a '$' prefix
Index: value.h
===================================================================
RCS file: /cvs/src/src/gdb/value.h,v
retrieving revision 1.108
diff -u -r1.108 value.h
--- value.h	4 Feb 2008 00:23:04 -0000	1.108
+++ value.h	1 Mar 2008 05:18:50 -0000
@@ -429,6 +429,8 @@
 
 extern struct value *access_value_history (int num);
 
+extern struct value *value_of_internalfunc (char *exp, int length);
+
 extern struct value *value_of_internalvar (struct internalvar *var);
 
 extern void set_internalvar (struct internalvar *var, struct value *val);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]