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]

RFA: Patch: implement missing macro functions


This morning I figured out that I could solve a longstanding gcc
debugging problem I've had using 'macro define'.  Then to my dismay I
discovered that this function was not implemented.

I suppose the point of having a stub function here is to get someone
like me to show up and implement it :-).  And, my annoyance at running
into the stub was at least partially counteracted by reading the code
and the docs... the macro expansion stuff all seems quite nice.

This patch implements macro define, macro undef, and macro list.
It adds some nominal test cases -- nothing major.

Now you can do things like:

macro define offsetof(T, F) ((int) &(((T *) 0)->F))

... which is quite handy.


In case you're curious, my use in gcc is to allow macro expansion of
the many gcc tree accessor macros, while avoiding the statement
expressions in the check macros that (of course) gdb does not
understand.  To do this I just redefine all the accessors with
gdb-safe ones, e.g.:

macro define TREE_TYPE(NODE) ((T)->common.type)

I haven't tested these yet, that's my next job.  Too bad that gdb
try/catch patch never went in... adding a lot of "macro define"s to
gcc's .gdbinit is problematic since they will all cause errors on
older gdbs :-(

So... ok to commit?

Tom

ChangeLog:
2008-05-20  Tom Tromey  <tromey@redhat.com>

	* macroscope.h (user_macro_scope): Declare.
	(default_macro_scope): Update documentation.
	* c-lang.c (c_preprocess_and_parse): Always attempt macro lookup.
	Use user_macro_scope.
	(null_macro_lookup): Remove.
	* macrotab.h (macro_define_user, macro_definition_delete,
	macro_user_macros): Declare.
	(macro_callback_fn): Declare.
	(macro_for_each): Likewise.
	* macrotab.c (macro_definition_delete): New function.
	(macro_tree_delete_value): Use it.
	(macro_define_user): New function.
	(macro_lookup_definition): Look in user-defined macro table.
	(foreach_macro): New function
	(macro_for_each): Likewise.
	* macroscope.c (user_macro_scope): New function.
	(default_macro_scope): Use it.
	* macroexp.h (macro_is_whitespace, macro_is_digit,
	macro_is_identifier_nondigit): Declare.
	* macroexp.c (macro_is_whitespace): Rename.  No longer static.
	(macro_is_digit): Likewise.
	(macro_is_identifier_nondigit): Likewise.
	(get_identifier): Update.
	(get_pp_number): Likewise.
	(get_token): Likewise.
	* macrocmd.c (skip_ws): New function.
	(extract_identifier): Likewise.
	(free_macro_definition_ptr): Likewise.
	(macro_user_macros): Rename from user_macros.  No longer static.
	(macro_define_command): Implement.
	(_initialize_macrocmd): Update.  Set main file on
	macro_user_macros.
	(macro_undef_command): Implement.
	(print_one_macro): New function.
	(macro_list_command): Implement.

testsuite/ChangeLog:
2008-05-20  Tom Tromey  <tromey@redhat.com>

	* gdb.base/macscp.exp: Add macro tests.

doc/ChangeLog:
2008-05-20  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Macros): Update.

Index: c-lang.c
===================================================================
RCS file: /cvs/src/src/gdb/c-lang.c,v
retrieving revision 1.53
diff -u -r1.53 c-lang.c
--- c-lang.c	6 Apr 2008 08:56:36 -0000	1.53
+++ c-lang.c	21 May 2008 00:25:07 -0000
@@ -261,13 +261,6 @@
 void *expression_macro_lookup_baton;
 
 
-static struct macro_definition *
-null_macro_lookup (const char *name, void *baton)
-{
-  return 0;
-}
-
-
 static int
 c_preprocess_and_parse (void)
 {
@@ -279,17 +272,11 @@
     scope = sal_macro_scope (find_pc_line (expression_context_pc, 0));
   else
     scope = default_macro_scope ();
+  if (! scope)
+    scope = user_macro_scope ();
 
-  if (scope)
-    {
-      expression_macro_lookup_func = standard_macro_lookup;
-      expression_macro_lookup_baton = (void *) scope;
-    }
-  else
-    {
-      expression_macro_lookup_func = null_macro_lookup;
-      expression_macro_lookup_baton = 0;      
-    }
+  expression_macro_lookup_func = standard_macro_lookup;
+  expression_macro_lookup_baton = (void *) scope;
 
   gdb_assert (! macro_original_text);
   make_cleanup (scan_macro_cleanup, 0);
Index: macrocmd.c
===================================================================
RCS file: /cvs/src/src/gdb/macrocmd.c,v
retrieving revision 1.13
diff -u -r1.13 macrocmd.c
--- macrocmd.c	1 Jan 2008 22:53:12 -0000	1.13
+++ macrocmd.c	21 May 2008 00:25:07 -0000
@@ -24,6 +24,7 @@
 #include "macroscope.h"
 #include "command.h"
 #include "gdbcmd.h"
+#include "gdb_string.h"
 
 
 /* The `macro' prefix command.  */
@@ -189,31 +190,161 @@
 
 /* User-defined macros.  */
 
+static void
+skip_ws (char **expp)
+{
+  while (macro_is_whitespace (**expp))
+    ++*expp;
+}
+
+static char *
+extract_identifier (char **expp)
+{
+  char *result;
+  char *p = *expp;
+  unsigned int len;
+  if (! *p || ! macro_is_identifier_nondigit (*p))
+    return NULL;
+  for (++p;
+       *p && (macro_is_identifier_nondigit (*p) || macro_is_digit (*p));
+       ++p)
+    ;
+  len = p - *expp;
+  result = (char *) xmalloc (len + 1);
+  memcpy (result, *expp, len);
+  result[len] = '\0';
+  *expp += len;
+  return result;
+}
+
+static void
+free_macro_definition_ptr (void *ptr)
+{
+  struct macro_definition **loc = (struct macro_definition **) ptr;
+  if (*loc)
+    macro_definition_delete (*loc);
+}
+
 /* A table of user-defined macros.  Unlike the macro tables used for
    symtabs, this one uses xmalloc for all its allocation, not an
    obstack, and it doesn't bcache anything; it just xmallocs things.  So
    it's perfectly possible to remove things from this, or redefine
    things.  */
-static struct macro_table *user_macros;
+struct macro_table *macro_user_macros;
 
 static void
 macro_define_command (char *exp, int from_tty)
 {
-  error (_("Command not implemented yet."));
+  struct macro_definition *new_macro = NULL;
+  char *name = NULL;
+  struct cleanup *cleanup_chain = make_cleanup (free_macro_definition_ptr,
+						&new_macro);
+  cleanup_chain = make_my_cleanup (&cleanup_chain, free_current_contents,
+				   &name);
+
+  new_macro = XZALLOC (struct macro_definition);
+  new_macro->table = macro_user_macros;
+
+  skip_ws (&exp);
+  name = extract_identifier (&exp);
+  if (! name)
+    error (_("Invalid macro name."));
+  if (*exp == '(')
+    {
+      /* Function-like macro.  */
+      int alloced = 5;
+      char **argv = (char **) xmalloc (alloced * sizeof (char *));
+
+      new_macro->kind = macro_function_like;
+      new_macro->argc = 0;
+      new_macro->argv = (const char * const *) argv;
+
+      /* Skip the '(' and whitespace.  */
+      ++exp;
+      skip_ws (&exp);
+
+      while (*exp != ')')
+	{
+	  int i;
+
+	  if (new_macro->argc == alloced)
+	    {
+	      alloced *= 2;
+	      argv = (char **) xrealloc (argv, alloced * sizeof (char *));
+	      /* Must update new_macro as well... */
+	      new_macro->argv = (const char * const *) argv;
+	    }
+	  argv[new_macro->argc] = extract_identifier (&exp);
+	  if (! argv[new_macro->argc])
+	    error (_("Macro is missing an argument."));
+	  ++new_macro->argc;
+
+	  for (i = new_macro->argc - 2; i >= 0; --i)
+	    {
+	      if (! strcmp (argv[i], argv[new_macro->argc - 1]))
+		error (_("Two macro arguments with identical names."));
+	    }
+
+	  skip_ws (&exp);
+	  if (*exp == ',')
+	    {
+	      ++exp;
+	      skip_ws (&exp);
+	    }
+	  else if (*exp != ')')
+	    error (_("',' or ')' expected at end of macro arguments."));
+	}
+      /* Skip the closing paren.  */
+      ++exp;
+    }
+  else
+    new_macro->kind = macro_object_like;
+
+  new_macro->replacement = xstrdup (exp);
+
+  macro_define_user (name, new_macro);
+  xfree (name);
+  discard_cleanups (cleanup_chain);
 }
 
 
 static void
 macro_undef_command (char *exp, int from_tty)
 {
-  error (_("Command not implemented yet."));
+  char *name;
+  skip_ws (&exp);
+  name = extract_identifier (&exp);
+  if (! name)
+    error (_("Invalid macro name."));
+  macro_undef (macro_main (macro_user_macros), -1, name);
+  xfree (name);
+}
+
+
+static void
+print_one_macro (const char *name, const struct macro_definition *macro)
+{
+  fprintf_filtered (gdb_stdout, "macro define %s", name);
+  if (macro->kind == macro_function_like)
+    {
+      int i;
+      fprintf_filtered (gdb_stdout, "(");
+      for (i = 0; i < macro->argc; ++i)
+	fprintf_filtered (gdb_stdout, "%s%s", (i > 0) ? ", " : "",
+			  macro->argv[i]);
+      fprintf_filtered (gdb_stdout, ")");
+    }
+  /* Note that we don't need a leading space here -- "macro define"
+     provided it.  */
+  fprintf_filtered (gdb_stdout, "%s\n", macro->replacement);
+  gdb_flush (gdb_stdout);
 }
 
 
 static void
 macro_list_command (char *exp, int from_tty)
 {
-  error (_("Command not implemented yet."));
+  macro_for_each (macro_user_macros, print_one_macro);
 }
 
 
@@ -274,5 +405,6 @@
 	   _("List all the macros defined using the `macro define' command."),
 	   &macrolist);
 
-  user_macros = new_macro_table (0, 0);
+  macro_user_macros = new_macro_table (0, 0);
+  macro_set_main (macro_user_macros, "<user-defined>");
 }
Index: macroexp.c
===================================================================
RCS file: /cvs/src/src/gdb/macroexp.c,v
retrieving revision 1.13
diff -u -r1.13 macroexp.c
--- macroexp.c	1 Jan 2008 22:53:12 -0000	1.13
+++ macroexp.c	21 May 2008 00:25:07 -0000
@@ -171,8 +171,8 @@
 /* Recognizing preprocessor tokens.  */
 
 
-static int
-is_whitespace (int c)
+int
+macro_is_whitespace (int c)
 {
   return (c == ' '
           || c == '\t'
@@ -182,15 +182,15 @@
 }
 
 
-static int
-is_digit (int c)
+int
+macro_is_digit (int c)
 {
   return ('0' <= c && c <= '9');
 }
 
 
-static int
-is_identifier_nondigit (int c)
+int
+macro_is_identifier_nondigit (int c)
 {
   return (c == '_'
           || ('a' <= c && c <= 'z')
@@ -255,13 +255,13 @@
 get_identifier (struct macro_buffer *tok, char *p, char *end)
 {
   if (p < end
-      && is_identifier_nondigit (*p))
+      && macro_is_identifier_nondigit (*p))
     {
       char *tok_start = p;
 
       while (p < end
-             && (is_identifier_nondigit (*p)
-                 || is_digit (*p)))
+             && (macro_is_identifier_nondigit (*p)
+                 || macro_is_digit (*p)))
         p++;
 
       set_token (tok, tok_start, p);
@@ -277,15 +277,15 @@
 get_pp_number (struct macro_buffer *tok, char *p, char *end)
 {
   if (p < end
-      && (is_digit (*p)
+      && (macro_is_digit (*p)
           || *p == '.'))
     {
       char *tok_start = p;
 
       while (p < end)
         {
-          if (is_digit (*p)
-              || is_identifier_nondigit (*p)
+          if (macro_is_digit (*p)
+              || macro_is_identifier_nondigit (*p)
               || *p == '.')
             p++;
           else if (p + 2 <= end
@@ -485,7 +485,7 @@
      only occur after a #include, which we will never see.  */
 
   while (p < end)
-    if (is_whitespace (*p))
+    if (macro_is_whitespace (*p))
       p++;
     else if (get_comment (tok, p, end))
       p += tok->len;
Index: macroexp.h
===================================================================
RCS file: /cvs/src/src/gdb/macroexp.h,v
retrieving revision 1.6
diff -u -r1.6 macroexp.h
--- macroexp.h	1 Jan 2008 22:53:12 -0000	1.6
+++ macroexp.h	21 May 2008 00:25:07 -0000
@@ -84,5 +84,11 @@
                          macro_lookup_ftype *lookup_func,
                          void *lookup_baton);
 
+/* Functions to classify characters according to cpp rules.  */
+
+int macro_is_whitespace (int c);
+int macro_is_identifier_nondigit (int c);
+int macro_is_digit (int c);
+
 
 #endif /* MACROEXP_H */
Index: macroscope.c
===================================================================
RCS file: /cvs/src/src/gdb/macroscope.c,v
retrieving revision 1.14
diff -u -r1.14 macroscope.c
--- macroscope.c	1 Jan 2008 22:53:12 -0000	1.14
+++ macroscope.c	21 May 2008 00:25:07 -0000
@@ -78,6 +78,16 @@
 
 
 struct macro_scope *
+user_macro_scope (void)
+{
+  struct macro_scope *ms;
+  ms = XNEW (struct macro_scope);
+  ms->file = macro_main (macro_user_macros);
+  ms->line = -1;
+  return ms;
+}
+
+struct macro_scope *
 default_macro_scope (void)
 {
   struct symtab_and_line sal;
@@ -110,7 +120,11 @@
       sal.line = cursal.line;
     }
 
-  return sal_macro_scope (sal);
+  ms = sal_macro_scope (sal);
+  if (! ms)
+    ms = user_macro_scope ();
+
+  return ms;
 }
 
 
Index: macroscope.h
===================================================================
RCS file: /cvs/src/src/gdb/macroscope.h,v
retrieving revision 1.6
diff -u -r1.6 macroscope.h
--- macroscope.h	1 Jan 2008 22:53:12 -0000	1.6
+++ macroscope.h	21 May 2008 00:25:07 -0000
@@ -39,12 +39,18 @@
 struct macro_scope *sal_macro_scope (struct symtab_and_line sal);
 
 
+/* Return a `struct macro_scope' object representing just the
+   user-defined macros.  The result is allocated using xmalloc; the
+   caller is responsible for freeing it.  */
+struct macro_scope *user_macro_scope (void);
+
 /* Return a `struct macro_scope' object describing the scope the `macro
    expand' and `macro expand-once' commands should use for looking up
    macros.  If we have a selected frame, this is the source location of
    its PC; otherwise, this is the last listing position.
 
-   If we have no macro information for the current location, return zero.
+   If we have no macro information for the current location, return
+   the user macro scope.
 
    The object returned is allocated using xmalloc; the caller is
    responsible for freeing it.  */
Index: macrotab.c
===================================================================
RCS file: /cvs/src/src/gdb/macrotab.c,v
retrieving revision 1.15
diff -u -r1.15 macrotab.c
--- macrotab.c	1 Jan 2008 22:53:12 -0000	1.15
+++ macrotab.c	21 May 2008 00:25:08 -0000
@@ -587,11 +587,9 @@
 }
 
 
-/* Free a macro definition.  */
-static void
-macro_tree_delete_value (void *untyped_definition)
+void
+macro_definition_delete (struct macro_definition *d)
 {
-  struct macro_definition *d = (struct macro_definition *) untyped_definition;
   struct macro_table *t = d->table;
 
   if (d->kind == macro_function_like)
@@ -608,6 +606,15 @@
 }
 
 
+/* Free a macro definition.  For use by splay trees.  */
+static void
+macro_tree_delete_value (void *untyped_definition)
+{
+  struct macro_definition *d = (struct macro_definition *) untyped_definition;
+  macro_definition_delete (d);
+}
+
+
 /* Find the splay tree node for the definition of NAME at LINE in
    SOURCE, or zero if there is none.  */
 static splay_tree_node
@@ -786,6 +793,25 @@
 
 
 void
+macro_define_user (const char *name, struct macro_definition *new_macro)
+{
+  struct macro_key *key;
+  splay_tree_node n = find_definition (name, new_macro->table->main_source, -1);
+
+  if (n)
+    {
+      key = (struct macro_key *) n->key;
+      splay_tree_remove (new_macro->table->definitions, n->key);
+    }
+
+  key = new_macro_key (new_macro->table, name, new_macro->table->main_source,
+		       -1);
+  splay_tree_insert (new_macro->table->definitions, (splay_tree_key) key,
+		     (splay_tree_value) new_macro);
+}
+
+
+void
 macro_undef (struct macro_source_file *source, int line,
              const char *name)
 {
@@ -845,7 +871,12 @@
 macro_lookup_definition (struct macro_source_file *source,
                          int line, const char *name)
 {
-  splay_tree_node n = find_definition (name, source, line);
+  splay_tree_node n;
+
+  /* Give user-defined macros priority over all others.  */
+  n = find_definition (name, macro_user_macros->main_source, -1);
+  if (! n)
+    n = find_definition (name, source, line);
 
   if (n)
     return (struct macro_definition *) n->value;
@@ -873,6 +904,24 @@
 }
 
 
+/* Helper function for macro_for_each.  */
+static int
+foreach_macro (splay_tree_node node, void *fnp)
+{
+  macro_callback_fn fn = (macro_callback_fn) fnp;
+  struct macro_key *key = (struct macro_key *) node->key;
+  struct macro_definition *def = (struct macro_definition *) node->value;
+  (*fn) (key->name, def);
+  return 0;
+}
+
+void
+macro_for_each (struct macro_table *table, macro_callback_fn fn)
+{
+  splay_tree_foreach (table->definitions, foreach_macro, fn);
+}
+
+
 
 /* Creating and freeing macro tables.  */
 
Index: macrotab.h
===================================================================
RCS file: /cvs/src/src/gdb/macrotab.h,v
retrieving revision 1.9
diff -u -r1.9 macrotab.h
--- macrotab.h	1 Jan 2008 22:53:12 -0000	1.9
+++ macrotab.h	21 May 2008 00:25:08 -0000
@@ -72,6 +72,8 @@
 /* A table of all the macro definitions for a given compilation unit.  */
 struct macro_table;
 
+/* The definition of a single macro.  */
+struct macro_definition;
 
 /* A source file that participated in a compilation unit --- either a
    main file, or an #included file.  If a file is #included more than
@@ -241,12 +243,23 @@
                             const char *replacement);
 
 
+/* Record a macro definition given a fully-filled macro_definition
+   object.  The object and its contents must be allocated
+   appropriately for the table into which the new macro will be
+   inserted; it is the caller's responsibility to ensure this.  A
+   previous macro definition of the same name will be silently
+   removed.  NAME is the name of the new macro; it is copied by this
+   function as needed.  */
+void macro_define_user (const char *name, struct macro_definition *new_macro);
+
 /* Record an #undefinition.
    Record in SOURCE's macro table that, at line number LINE in SOURCE,
    we removed the definition for the preprocessor symbol named NAME.  */
 void macro_undef (struct macro_source_file *source, int line,
                   const char *name);
 
+/* Delete a macro_definition object.  */
+void macro_definition_delete (struct macro_definition *obj);
 
 /* Different kinds of macro definitions.  */
 enum macro_kind
@@ -298,5 +311,16 @@
                             const char *name,
                             int *definition_line));
 
+/* The table of macros defined by the user.  */
+extern struct macro_table *macro_user_macros;
+
+
+/* Callback function when walking a macro table.  */
+typedef void (*macro_callback_fn) (const char *,
+				   const struct macro_definition *);
+
+/* Call a function for each macro in the table.  */
+void macro_for_each (struct macro_table *, macro_callback_fn);
+
 
 #endif /* MACROTAB_H */
Index: testsuite/gdb.base/macscp.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/macscp.exp,v
retrieving revision 1.7
diff -u -r1.7 macscp.exp
--- testsuite/gdb.base/macscp.exp	3 May 2008 22:30:51 -0000	1.7
+++ testsuite/gdb.base/macscp.exp	21 May 2008 00:25:09 -0000
@@ -428,3 +428,23 @@
 gdb_test "print M" \
     "No symbol \"M\" in current context\." \
     "print expression with macro after undef."
+
+gdb_test "macro define M 5" \
+  "" \
+  "basic macro define"
+
+gdb_test "print M" \
+  " = 5" \
+  "expansion of defined macro"
+
+gdb_test "macro list" \
+  "macro define M 5" \
+  "basic macro list"
+
+gdb_test "macro undef M" \
+  "" \
+  "basic macro undef"
+
+gdb_test "print M" \
+    "No symbol \"M\" in current context\." \
+    "print expression with macro after user undef."
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.495
diff -u -r1.495 gdb.texinfo
--- doc/gdb.texinfo	9 May 2008 17:02:01 -0000	1.495
+++ doc/gdb.texinfo	21 May 2008 00:25:12 -0000
@@ -1,6 +1,6 @@
 \input texinfo      @c -*-texinfo-*-
 @c Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
-@c 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+@c 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
 @c Free Software Foundation, Inc.
 @c
 @c %**start of header
@@ -7822,12 +7822,12 @@
 @cindex macros, user-defined
 @item macro define @var{macro} @var{replacement-list}
 @itemx macro define @var{macro}(@var{arglist}) @var{replacement-list}
-@i{(This command is not yet implemented.)}  Introduce a definition for a
-preprocessor macro named @var{macro}, invocations of which are replaced
-by the tokens given in @var{replacement-list}.  The first form of this
-command defines an ``object-like'' macro, which takes no arguments; the
-second form defines a ``function-like'' macro, which takes the arguments
-given in @var{arglist}.
+Introduce a definition for a preprocessor macro named @var{macro},
+invocations of which are replaced by the tokens given in
+@var{replacement-list}.  The first form of this command defines an
+``object-like'' macro, which takes no arguments; the second form
+defines a ``function-like'' macro, which takes the arguments given in
+@var{arglist}.
 
 A definition introduced by this command is in scope in every expression
 evaluated in @value{GDBN}, until it is removed with the @command{macro
@@ -7837,16 +7837,14 @@
 
 @kindex macro undef
 @item macro undef @var{macro}
-@i{(This command is not yet implemented.)}  Remove any user-supplied
-definition for the macro named @var{macro}.  This command only affects
-definitions provided with the @command{macro define} command, described
-above; it cannot remove definitions present in the program being
-debugged.
+Remove any user-supplied definition for the macro named @var{macro}.
+This command only affects definitions provided with the @command{macro
+define} command, described above; it cannot remove definitions present
+in the program being debugged.
 
 @kindex macro list
 @item macro list
-@i{(This command is not yet implemented.)}  List all the macros
-defined using the @code{macro define} command.
+List all the macros defined using the @code{macro define} command.
 @end table
 
 @cindex macros, example of debugging with


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