[patch] Indirect access to GDB history variables

Steve Rodrigues steverod@netapp.com
Fri Dec 15 02:41:00 GMT 2006


New Feature for GDB: Programmatic access to the value history.

Problem Description: Our company has a wide variety of GDB scripts used to 
analyze problems within core files. Many of these scripts will generate values
that are useful to probe into later; however, the scripts will generate a LOT
of values, or values that aren't in sequential order (you care about, say,
every third value). GDB only lets you reference previous history value either
with absolute numbers ($10, $236) or with reference to the most recently printed value ($$, $$9, etc). It sure would be nice if there was a way to be able to
access the value history by indirecting through a variable.

Feature: This patch enables users to programmatcially access the value history
through a GDB variable, by overloading the "$$" construct to contain a variable
name. For example, if my script had printed out values $10-$27, but only every
3rd one was interesting (it was a pointer I wished to examine further), I could
do the following:

	set $i=10
	while ($i < 28)
		p *$$i
		set $i+= 3
	end

... and I'd see values of $10, $13, $16 and so on. This makes it easier to
compose scripts together when debugging.

Implementation:
The meat of the change is in parse.c (write_dollar_variable) and eval.c 
(evaluate_subexp_standard).

I introduced a new operand type, OP_LAST_INTERNALVAR, which is emitted
by write_dollar_variable (parse.c) when it sees a "$$" that is NOT
followed by digits (and not alone, which is already an OP_LAST
operand). OP_LAST_INTERNALVAR should always be followed by an
OP_INTERNALVAR operand, pointing to the GDB variable which we will
indirect through.

When evaluate_subexp_standard (eval.c) sees an OP_LAST_INTERNALVAR, we just
need to evaluate the following OP_INTERNALVAR expression and take the 
value_contents of that expression, passing the results into 
access_value_history.

We have been using this modification internally for over a year on gdb 6.2
with no problems. The patch has been verified on gdb-6.6.50.20061212
(most recent weekly build) on Linux and on gdb-6.6.50.20061127 on Solaris 5.8
(couldn't get 20061212 to build).

Testing: This has been tested by hand. I've been trying to write a test
 case but have been having no luck getting the test suite to run (due to 
old versions of Tcl/expect on the systems this was developed on).

Future Directions: The obvious next step for this is to add a variable ($#?)
which indicates the current history value count, which makes this amenable to
full scripting rather than use by hand.

ChangeLog and Patch are attached.

--Steve

-- 
Steven Rodrigues           | Lost, yesterday, somewhere between sunrise and 
Member of Technical Staff  | sunset, two golden hours, each set with sixty
Network Appliance Corp.    | diamond minutes. No reward is offered, for they
steverod@netapp.com        | are gone forever. -- Horace Mann

-------------- next part --------------
2006-10-17	Steve Rodrigues <steverod@netapp.com>
	* expression.h (enum exp_opcode): Add OP_LAST_INTERNALVAR opcode.
	* eval.c (evaluate_subexp_standard): Process a OP_LAST_INTERNALVAR
	operand to indirectly access the value history.
	* ada-lang.c (resolve_subexp): Support OP_LAST_INTERNALVAR.
	* expprint.c (print_subexp_standard): Support OP_LAST_INTERNALVAR.
	(op_name_standard): Support OP_LAST_INTERNALVAR. 
	(dump_subexp_body_standard): Support OP_LAST_INTERNALVAR.
	* parse.c (write_dollar_variable): Support '$$' to be treated as
	an OP_LAST_INTERNALVAR.
	
	
	
-------------- next part --------------
--- ./gdb/ada-lang.c.orig	Thu Nov 30 16:32:29 2006
+++ ./gdb/ada-lang.c	Thu Dec 14 18:01:00 2006
@@ -2728,6 +2728,11 @@ resolve_subexp (struct expression **expp
       *pos += 3;
       break;
 
+    case OP_LAST_INTERNALVAR:
+      *pos ++;
+      nargs = 1;
+      break;
+          
     case UNOP_MEMVAL:
       *pos += 3;
       nargs = 1;
--- ./gdb/eval.c.orig	Mon Oct  9 20:17:53 2006
+++ ./gdb/eval.c	Thu Dec 14 18:01:00 2006
@@ -473,6 +473,12 @@ evaluate_subexp_standard (struct type *e
       return
 	access_value_history (longest_to_int (exp->elts[pc + 1].longconst));
 
+     case OP_LAST_INTERNALVAR:
+       {
+       arg1 = evaluate_subexp(NULL_TYPE, exp, pos, noside);
+       return access_value_history(*(int*)value_contents(arg1));
+       }
+
     case OP_REGISTER:
       {
 	int regno = longest_to_int (exp->elts[pc + 1].longconst);
--- ./gdb/expprint.c.orig	Mon Oct  9 20:17:53 2006
+++ ./gdb/expprint.c	Thu Dec 14 18:01:00 2006
@@ -151,6 +151,12 @@ print_subexp_standard (struct expression
 			internalvar_name (exp->elts[pc + 1].internalvar));
       return;
 
+     case OP_LAST_INTERNALVAR:
+       (*pos) += 2;
+       fprintf_filtered (stream, "$$%s",
+                       internalvar_name (exp->elts[pc + 1].internalvar));
+       return;
+ 
     case OP_FUNCALL:
       (*pos) += 2;
       nargs = longest_to_int (exp->elts[pc + 1].longconst);
@@ -695,6 +701,8 @@ op_name_standard (enum exp_opcode opcode
       return "OP_REGISTER";
     case OP_INTERNALVAR:
       return "OP_INTERNALVAR";
+    case OP_LAST_INTERNALVAR:
+      return "OP_LAST_INTERNALVAR";
     case OP_FUNCALL:
       return "OP_FUNCALL";
     case OP_STRING:
@@ -976,6 +984,9 @@ dump_subexp_body_standard (struct expres
       fprintf_filtered (stream, " (%s)",
 			exp->elts[elt].internalvar->name);
       elt += 2;
+      break;
+    case OP_LAST_INTERNALVAR:
+      fprintf_filtered (stream, "History element from Internal var");
       break;
     case OP_FUNCALL:
       {
--- ./gdb/expression.h.orig	Mon Oct  9 20:17:53 2006
+++ ./gdb/expression.h	Thu Dec 14 18:01:00 2006
@@ -174,6 +174,12 @@ enum exp_opcode
     /* OP_INTERNALVAR is followed by an internalvar ptr in the next exp_element.
        With another OP_INTERNALVAR at the end, this makes three exp_elements.  */
     OP_INTERNALVAR,
+    
+    /* OP_LAST_INTERNALVAR is used for accessing the history programmatically.
+       It is followed by an internalvar ptr in the next exp_element.
+       With another OP_LAST_INTERNALVAR at the end, this makes three 
+       exp_elements. */
+    OP_LAST_INTERNALVAR,
 
     /* OP_FUNCALL is followed by an integer in the next exp_element.
        The integer is the number of args to the function call.
--- ./gdb/parse.c.orig	Sat Nov 18 15:54:32 2006
+++ ./gdb/parse.c	Thu Dec 14 18:01:00 2006
@@ -476,6 +476,7 @@ write_dollar_variable (struct stoken str
 {
   struct symbol *sym = NULL;
   struct minimal_symbol *msym = NULL;
+  int stroff = 1;
 
   /* Handle the tokens $digits; also $ (short for $0) and $$ (short for $$1)
      and $$digits (equivalent to $<-digits> if you could type that). */
@@ -487,7 +488,7 @@ write_dollar_variable (struct stoken str
   if (str.length >= 2 && str.ptr[1] == '$')
     {
       negate = 1;
-      i = 2;
+      i = stroff = 2;
     }
   if (i == str.length)
     {
@@ -536,11 +537,17 @@ write_dollar_variable (struct stoken str
       return;
     }
 
-  /* Any other names starting in $ are debugger internal variables.  */
+  /* Any other names starting in $ are debugger internal variables.  
+     A 'negated' internal variable should be treated as an indirect
+     history reference, which adds another LAST_INTERNALVAR opcode */
 
   write_exp_elt_opcode (OP_INTERNALVAR);
-  write_exp_elt_intern (lookup_internalvar (copy_name (str) + 1));
+  write_exp_elt_intern (lookup_internalvar (copy_name (str) + stroff));
   write_exp_elt_opcode (OP_INTERNALVAR);
+
+  if (negate) 
+       write_exp_elt_opcode (OP_LAST_INTERNALVAR);
+
   return;
 handle_last:
   write_exp_elt_opcode (OP_LAST);
@@ -904,6 +911,10 @@ operator_length_standard (struct express
       oplen = 3;
       break;
 
+    case OP_LAST_INTERNALVAR:
+      oplen = 1;
+      args  = 1;
+      break;
     case OP_COMPLEX:
       oplen = 1;
       args = 2;


More information about the Gdb-patches mailing list