Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* RFC add "convenience functions" to gdb
@ 2008-03-01  5:50 Tom Tromey
  2008-03-01  7:05 ` Tom Tromey
  2008-03-08 20:24 ` Tom Tromey
  0 siblings, 2 replies; 5+ messages in thread
From: Tom Tromey @ 2008-03-01  5:50 UTC (permalink / raw)
  To: gdb-patches

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 @@
     }
 }
 \f
+/* 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);
+}
+
+\f
 /* 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);


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: RFC add "convenience functions" to gdb
  2008-03-01  5:50 RFC add "convenience functions" to gdb Tom Tromey
@ 2008-03-01  7:05 ` Tom Tromey
  2008-03-08 20:24 ` Tom Tromey
  1 sibling, 0 replies; 5+ messages in thread
From: Tom Tromey @ 2008-03-01  7:05 UTC (permalink / raw)
  To: gdb-patches

>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

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

Of course, after sending this, I remembered that Roland told me how,
by setting multiple breakpoints and having the breakpoint in the outer
frame set a convenience variable.

I never remember this when actually debugging, though.  And, I think
convenience functions will have other uses.  So, please consider the
patch even though my example was a bad one.

Tom


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: RFC add "convenience functions" to gdb
  2008-03-01  5:50 RFC add "convenience functions" to gdb Tom Tromey
  2008-03-01  7:05 ` Tom Tromey
@ 2008-03-08 20:24 ` Tom Tromey
  2008-03-08 20:30   ` Daniel Jacobowitz
  1 sibling, 1 reply; 5+ messages in thread
From: Tom Tromey @ 2008-03-08 20:24 UTC (permalink / raw)
  To: gdb-patches

>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

Tom> So, this evening I spent some time adding a new "convenience
Tom> function" facility to gdb.

I added a new convenience function, "show".  This lets you get at any
gdb "set/show" value in an expression.  Mostly, I think, this kind of
thing is useful for conditions in commands.

I've appended the new function, so folks can see it.  I could prepare
a new patch if anybody cares.

After writing this I started thinking that it sure would be nice to
have a host-side string type, plus a regex-matching operator.  Then
$(caller-matches ...) could be rewritten like:

    break func if $(caller-name) =~ $"callerfunction"

I use $"..." to denote a host string.

After this it would be pretty simple to start allowing scripting
access to all kinds of interesting state: string-valued set/show
variables, the exe name, the names of functions up and down the stack,
types, syscalls (whatever happened to that "catch syscall" patch?) --
anything we can think of.

So, I started implementing host strings... but I dunno.  Is it worth
doing?  Maybe python will solve all the problems -- since what I
really want is a nice, normal, complete programming language with
access to gdb state.

In fact maybe we only need *one* convenience function, $(python ...).

Tom

/* Return the value of a 'set/show' variable.  Integer-ish variables
   are returned as integers, others as strings.  */
static struct value *
show_fn (char *arg)
{
  struct cmd_list_element *alias, *prefix, *cmd;
  char *newarg;

  newarg = concat ("show ", arg, (char *) NULL);
  make_cleanup (free, newarg);

  if (! lookup_cmd_composition (newarg, &alias, &prefix, &cmd))
    error ("could not find variable `%s'", arg);
  if (! cmd->var)
    error ("`%s' is not a variable", arg);
  switch (cmd->var_type)
    {
    case var_string:
    case var_string_noescape:
    case var_optional_filename:
    case var_filename:
    case var_enum:
      error ("gdb-internal string type not yet implemented");

    case var_boolean:
      return value_from_longest (builtin_type_bool, * (int *) cmd->var);

    case var_auto_boolean:
      {
	enum auto_boolean ab = * (enum auto_boolean *) cmd->var;
	return value_from_longest (builtin_type_int,
				   (ab == AUTO_BOOLEAN_TRUE ? 1
				    : (ab == AUTO_BOOLEAN_FALSE ? 0
				       : -1)));
      }

    case var_integer:
    case var_uinteger:
    case var_zinteger:
      return value_from_longest (builtin_type_int, * (int *) cmd->var);
    }

  error ("programmer error: unhandled type");
}


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: RFC add "convenience functions" to gdb
  2008-03-08 20:24 ` Tom Tromey
@ 2008-03-08 20:30   ` Daniel Jacobowitz
  2008-03-08 20:35     ` Tom Tromey
  0 siblings, 1 reply; 5+ messages in thread
From: Daniel Jacobowitz @ 2008-03-08 20:30 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Sat, Mar 08, 2008 at 12:31:52PM -0700, Tom Tromey wrote:
> I use $"..." to denote a host string.
> 
> After this it would be pretty simple to start allowing scripting
> access to all kinds of interesting state: string-valued set/show
> variables, the exe name, the names of functions up and down the stack,
> types, syscalls (whatever happened to that "catch syscall" patch?) --
> anything we can think of.

Hold on a bit; I promised to post my lazy strings patch for Doug and I
have so far failed to do so.  We can use the normal string syntax, as
long as we don't flush them out to the target unless some operation
requires it.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: RFC add "convenience functions" to gdb
  2008-03-08 20:30   ` Daniel Jacobowitz
@ 2008-03-08 20:35     ` Tom Tromey
  0 siblings, 0 replies; 5+ messages in thread
From: Tom Tromey @ 2008-03-08 20:35 UTC (permalink / raw)
  To: gdb-patches

>>>>> "Daniel" == Daniel Jacobowitz <drow@false.org> writes:

Daniel> Hold on a bit; I promised to post my lazy strings patch for Doug and I
Daniel> have so far failed to do so.  We can use the normal string syntax, as
Daniel> long as we don't flush them out to the target unless some operation
Daniel> requires it.

Nice.  I did not even consider this idea, probably because I don't
actually know anything about gdb internals :-).  Though, I was
considering auto-coercion of host strings to target strings... which I
suppose in the end amounts to the same thing.

Anyhow, it isn't a rush for me.  I'm really just tinkering a little.

Tom


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2008-03-08 20:35 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-03-01  5:50 RFC add "convenience functions" to gdb Tom Tromey
2008-03-01  7:05 ` Tom Tromey
2008-03-08 20:24 ` Tom Tromey
2008-03-08 20:30   ` Daniel Jacobowitz
2008-03-08 20:35     ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox