[RFA] New substitute-path commands

Joel Brobecker
2006-07-05T21:58:01Z

[-- Attachment #1: Type: text/plain, Size: 3983 bytes --]

Hello,

Following up on a discussing that took place on the gdb@ mailing list:

http://sources.redhat.com/ml/gdb/2006-06/msg00192.html

I implemented a new set of commands that should be useful in the case
when a developer debugs a program of which the source tree has been
moved to a different location from where it was compiled.

The new commands are:

(gdb) substitute-path add <from> <to>
(gdb) substitute-path remove
(gdb) substitute-path

* "substitute-path add" adds a substitution rule, where the first
instance of <from> is replaced by <to>.

* "substitute-path remove" removes the current substitution rule
if one was specified. No effect otherwise.

* "substitute-path" prints the current substitution rule.

Some users argued that "substitute-path show" should also be added.
I think it's mostly aesthetic, and hence passed on this suggestion,
but I can do that as well if others think it'd be nice to have it.

At least one user suggested that we provide the possibility of applying
a list of substitution rules as opposed to one. I'm not against that
idea, but this makes the design and implementation slightly more complex
for something that I think will be little used. I suspect that most
people will use it in a very specific context where the entire source
tree has been moved - so one substitution rule should be enough.

Nonetheless, should anybody require this feature, there is an upward
compatible way of enhancing my implementation later on. The "add"
command remains the same. The "remove" command without any argument
would delete all substitution rules. And with a "from" argument, it
would remove the rule that matches "from". The "substitute-path"
command would print all substitution rules, not just the one.

Here is how I tested it:

* In a root directory, I have some sources in src/
* cd to src/, and compile the sources
* cd <root>, cp -R src dup
* cd src/, modify one line of my source file
* gdb dup/my_program
(gdb) substitute-path add src dup
(gdb) list my_program.c:1

With the current debugger, GDB will list src/my_program.c. But in
fact, this is no longer the file that was used to build the program
we're actually debugging. With the patch I am suggesting, the debugger
lists dup/my_program.c.

The "substitute-path" has two uses that I can see, each of them when
the path to the source tree available to GDB is different from the path
when the program was built:

. 1: The path to the source tree is no longer valid for whatever
reason. In that case, the "dir" command can be used to point
GDB to the current location. But larger programs may use a
full tree of directories, making it painful because one has
to use a series of "dir" commands, one per directory. This
new command makes it easier.

. 2: The path to the source tree is still valid. This is the case
I explained in my message on gdb@. Please refer to it for more
details. In this case, this new command is the only way to make
sure GDB uses the right sources.

Question: Is a MI equivalent needed?

2006-07-05 Joel Brobecker <brobecker@adacore.com

* source.c (substitute_path_from): New static variable.
(substitute_path_to): Likewise.
(substitute_path_cmdlist): Likewise.
(xrewrite_source_path): New function.
(find_and_open_source): Add source path rewriting support.
(substitute_path_command): New function.
(xextract_path_argument): New function.
(substitute_path_remove_command): New function.
(substitute_path_add_command): New function.
(_initialize_source): Add new substitute-path commands.

Tested on x86-linux, no regression. I wouldn't mind adding a testcase
for this new feature, but I'm not sure yet how to achieve this.

Documentation & NEWS update will be sent if the feature is accepted.

Thanks,
--
Joel

[-- Attachment #2: subst.diff --]
[-- Type: text/plain, Size: 8646 bytes --]

Index: source.c
===================================================================
RCS file: /cvs/src/src/gdb/source.c,v
retrieving revision 1.75
diff -u -p -r1.75 source.c
--- source.c 15 May 2006 15:50:13 -0000 1.75
+++ source.c 5 Jul 2006 21:46:11 -0000
@@ -73,6 +73,13 @@ static void show_directories (char *, in

char *source_path;

+/* Support for source path substitution commands. */
+
+static char *substitute_path_from = NULL;
+static char *substitute_path_to = NULL;
+
+static struct cmd_list_element *substitute_path_cmdlist;
+
/* Symtab of default file for listing lines of. */

static struct symtab *current_source_symtab;
@@ -828,6 +835,47 @@ source_full_path_of (char *filename, cha
return 1;
}

+/* If the user specified a source path substitution rule, then
+ try applying it on PATH, and return the new path. This new
+ path must be deallocated afterwards.
+
+ Return NULL if no substitution rule was specified by the user,
+ or of this rule didn't apply to the given PATH. */
+
+static char *
+xrewrite_source_path (const char *path)
+{
+ char *from_start;
+ char *new_path;
+
+ /* If no path substitution rule was specified, then no rewrite
+ is actually needed. */
+
+ if (substitute_path_from == NULL)
+ return NULL;
+
+ /* Search for the first occurence of SUBSTITUTE_PATH_FROM.
+ No substitution needed of not found. */
+
+ from_start = strstr (path, substitute_path_from);
+
+ if (from_start == NULL)
+ return NULL;
+
+ /* Compute the rewritten path and return it. */
+
+ new_path = (char *) malloc (strlen (path) + 1
+ + strlen (substitute_path_to)
+ - strlen (substitute_path_from));
+ strncpy (new_path, path, from_start - path);
+ strcpy (new_path + (from_start - path),
+ substitute_path_to);
+ strcat (new_path,
+ from_start + strlen (substitute_path_from));
+
+ return new_path;
+}
+
/* This function is capable of finding the absolute path to a
source file, and opening it, provided you give it an
OBJFILE and FILENAME. Both the DIRNAME and FULLNAME are only
@@ -844,7 +892,7 @@ source_full_path_of (char *filename, cha
FULLNAME is set to the absolute path to the file just opened.

On Failure
- A non valid file descriptor is returned. ( the return value is negitive )
+ A non valid file descriptor is returned. ( the return value is negative )
FULLNAME is set to NULL. */
int
find_and_open_source (struct objfile *objfile,
@@ -857,8 +905,22 @@ find_and_open_source (struct objfile *ob
int result;

/* Quick way out if we already know its full name */
+
if (*fullname)
{
+ {
+ /* The user may have requested that source paths be rewritten
+ according to a substitution rule he provided. Check if
+ this is the case, and do the rewrite if appropriate. */
+ char *rewritten_fullname = xrewrite_source_path (*fullname);
+
+ if (rewritten_fullname != NULL)
+ {
+ xfree (*fullname);
+ *fullname = rewritten_fullname;
+ }
+ }
+
result = open (*fullname, OPEN_MODE);
if (result >= 0)
return result;
@@ -869,6 +931,27 @@ find_and_open_source (struct objfile *ob

if (dirname != NULL)
{
+ /* If necessary, rewrite the compilation directory name according
+ to the source path substitution rules specified by the user.
+
+ Normally, we should really try to do the rewrite on the entire
+ path as opposed to just the directory name, but that's making
+ things much more complicated since we now have to concat the
+ dirname and filename, apply the substitution rule, and then do
+ [...] ??? How do we split the path back into dirname and filename?
+ In practice, the source path substitution feature should be used
+ when the entire tree has been moved, in which case only the root
+ part of the tree path will need to be rewritten. So this should
+ not be a problem in practice. */
+
+ char *rewritten_dirname = xrewrite_source_path (dirname);
+
+ if (rewritten_dirname != NULL)
+ {
+ make_cleanup (xfree, rewritten_dirname);
+ dirname = rewritten_dirname;
+ }
+
/* Replace a path entry of $cdir with the compilation directory name */
#define cdir_len 5
/* We cast strstr's result in case an ANSIhole has made it const,
@@ -1587,6 +1670,129 @@ reverse_search_command (char *regex, int
fclose (stream);
return;
}
+
+/* Print the current source path substitution rule. */
+
+static void
+substitute_path_command (char *args, int from_tty)
+{
+ if (substitute_path_from == NULL)
+ printf_filtered (_("No source path substitution rule specified.\n"));
+ else
+ printf_filtered (_("`%s' in source paths now substituted with `%s'.\n"),
+ substitute_path_from, substitute_path_to);
+}
+
+/* Extract the first argument (possibly quoted) from ARGS, and return it.
+ ARGS is updated to point right after that first argument.
+ The returned value must be deallocated afterwards.
+
+ Return NULL is an argument could not be found. */
+
+static char *
+xextract_path_argument (char **args)
+{
+ int is_quoted;
+ char *p;
+ int arg_len;
+ char *path;
+
+ /* First, skip any leading white spaces, and check that we have
+ at least one character left after the trimming. */
+
+ while (isspace (**args))
+ (*args)++;
+
+ if (**args == '\0')
+ return NULL;
+
+ /* Find the first character after the first argument. */
+
+ p = skip_quoted_chars (*args, NULL, " \t");
+ arg_len = p - *args;
+
+ /* If the path was quoted, then remove the quotes. */
+
+ is_quoted = (strchr (get_gdb_completer_quote_characters (), **args) != NULL);
+ if (is_quoted)
+ {
+ if (strchr (get_gdb_completer_quote_characters (),
+ (*args)[arg_len - 1]) == NULL)
+ error (_("missing closing quote in argument: %s"), *args);
+
+ arg_len -= 2;
+ (*args)++;
+ }
+
+ /* If the argument is empty, then there is no real path, and hence
+ treat this as if the argumet was missing. */
+
+ if (arg_len < 1)
+ return NULL;
+
+ path = (char *) xmalloc ((arg_len + 1) * sizeof (char));
+ strncpy (path, *args, arg_len);
+ path[arg_len] = '\0';
+
+ *args = p;
+
+ return path;
+}
+
+/* Delete the current source path substitution rule. */
+
+static void
+substitute_path_remove_command (char *args, int from_tty)
+{
+ if (substitute_path_from != NULL)
+ {
+ xfree (substitute_path_from);
+ substitute_path_from = NULL;
+ }
+
+ if (substitute_path_to != NULL)
+ {
+ xfree (substitute_path_to);
+ substitute_path_to = NULL;
+ }
+}
+
+/* Add a new source path substitution rule. */
+
+static void
+substitute_path_add_command (char *args, int from_tty)
+{
+ char *from_path, *to_path;
+
+ /* Extract the arguments from the command line. */
+
+ if (args == NULL)
+ error (_("Argument missing in command"));
+
+ from_path = xextract_path_argument (&args);
+ if (from_path == NULL)
+ error (_("Argument missing in command"));
+
+ to_path = xextract_path_argument (&args);
+ if (to_path == NULL)
+ {
+ xfree (from_path);
+ error (_("Argument missing in command"));
+ }
+
+ /* Remove any previous substitution rule. */
+
+ substitute_path_remove_command (NULL, from_tty);
+
+ /* Insert the new substitution rule, and print a confirmation message
+ to the user. */
+
+ substitute_path_from = from_path;
+ substitute_path_to = to_path;
+
+ substitute_path_command (NULL, from_tty);
+}
+
\f
void
_initialize_source (void)
@@ -1666,4 +1872,22 @@ Show number of source lines gdb will lis
NULL,
show_lines_to_list,
&setlist, &showlist);
+
+ add_prefix_cmd ("substitute-path", class_files, substitute_path_command,
+ _("Show the current source path substitution rule."),
+ &substitute_path_cmdlist, "substitute-path ",
+ 0 /* allow-unknown */, &cmdlist);
+
+ add_cmd ("add", class_files, substitute_path_add_command,
+ _("\
+Add a source path substitution rule. If a substitution rule was previously\n\
+set, it is overridden."),
+ &substitute_path_cmdlist);
+
+ add_cmd ("remove", class_files, substitute_path_remove_command,
+ _("\
+Remove the current source path substitution rule. Has no effect\n\
+if no path substitution rule was previously specified."),
+ &substitute_path_cmdlist);
+
}