From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 27024 invoked by alias); 1 Mar 2008 05:50:10 -0000 Received: (qmail 27011 invoked by uid 22791); 1 Mar 2008 05:50:08 -0000 X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (66.187.233.31) by sourceware.org (qpsmtpd/0.31) with ESMTP; Sat, 01 Mar 2008 05:49:51 +0000 Received: from int-mx1.corp.redhat.com (int-mx1.corp.redhat.com [172.16.52.254]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id m215nn8h018676 for ; Sat, 1 Mar 2008 00:49:49 -0500 Received: from pobox.corp.redhat.com (pobox.corp.redhat.com [10.11.255.20]) by int-mx1.corp.redhat.com (8.13.1/8.13.1) with ESMTP id m215nmtV005923 for ; Sat, 1 Mar 2008 00:49:48 -0500 Received: from opsy.redhat.com (vpn-14-132.rdu.redhat.com [10.11.14.132]) by pobox.corp.redhat.com (8.13.1/8.13.1) with ESMTP id m215nlK4014789; Sat, 1 Mar 2008 00:49:47 -0500 Received: by opsy.redhat.com (Postfix, from userid 500) id 133B13780C1; Fri, 29 Feb 2008 21:59:47 -0700 (MST) To: gdb-patches@sources.redhat.com Subject: RFC add "convenience functions" to gdb From: Tom Tromey Reply-To: Tom Tromey X-Attribution: Tom Date: Sat, 01 Mar 2008 05:50:00 -0000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-03/txt/msg00001.txt.bz2 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 * 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) : New case. * expression.h (enum exp_opcode) : New constant. * expprint.c (print_subexp_standard) : New case. (op_name_standard) : Likewise. (dump_subexp_body_standard) : Likewise. * eval.c (evaluate_subexp_standard) : New case. * c-exp.y (yylex): Recognize $(...). * ax-gdb.c (gen_expr) : 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 + /* 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);