RFC: add relative file name handling for linespecs

Tom Tromey
2012-01-10T16:06:42Z

I'd appreciate comments on this patch.

This patch changes gdb to allow relative file names to match a source
file name if all the trailing components match. Previously this
behavior was only allowed if the source file name did not have any
directory separators.

That is, now "break gcc/expr.c:1000" will work as many users expect.
See the recent thread on gdb@.

This needs at least a doc review.

Built and regtested on x86-64 F15.

Tom

2012-01-10 Tom Tromey <tromey@redhat.com>

* NEWS: Add item.
* symtab.h (compare_filenames_for_search): Declare.
* symtab.c (compare_filenames_for_search): New function.
(iterate_over_some_symtabs): Use it.
* symfile.h (struct quick_symbol_functions)
<map_symtabs_matching_filename>: Change spec.
* psymtab.c (partial_map_symtabs_matching_filename): Use
compare_filenames_for_search. Update for new spec.
* dwarf2read.c (dw2_map_symtabs_matching_filename): Use
compare_filenames_for_search. Update for new spec.

2012-01-10 Tom Tromey <tromey@redhat.com>

* gdb.texinfo (Specify Location): Document relative file name
handling.

2012-01-10 Tom Tromey <tromey@redhat.com>

* gdb.linespec/linespec.exp: Change some tests to use $decimal.
Add test for relative directory.

diff --git a/gdb/NEWS b/gdb/NEWS
index a9a7859..10661c1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9,6 +9,11 @@
* The binary "gdbtui" can no longer be built or installed.
Use "gdb -tui" instead.

+* The filename part of a linespec will now match trailing components
+ of a source file name. For example, "break gcc/expr.c:1000" will
+ now set a breakpoint in build/gcc/expr.c, but not
+ build/libcpp/expr.c.
+
*** Changes in GDB 7.4

* GDB now handles ambiguous linespecs more consistently; the existing
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 2f4aa4f..1f3b2a1 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -6492,6 +6492,11 @@ linespec.

@item @var{filename}:@var{linenum}
Specifies the line @var{linenum} in the source file @var{filename}.
+If @var{filename} is a relative file name, then it will match any
+source file name with the same trailing components. For example, if
+@var{filename} is @samp{gcc/expr.c}, then it will match source file
+name of @file{/build/trunk/gcc/expr.c}, but not
+@file{/build/trunk/libcpp/expr.c} or @file{/build/trunk/gcc/x-expr.c}.

@item @var{function}
Specifies the line that begins the body of the function @var{function}.
diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index d09b27a..861b8de 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -2402,8 +2402,8 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
{
int i;
const char *name_basename = lbasename (name);
- int check_basename = name_basename == name;
- struct dwarf2_per_cu_data *base_cu = NULL;
+ int name_len = strlen (name);
+ int is_abs = IS_ABSOLUTE_PATH (name);

dw2_setup (objfile);

@@ -2426,7 +2426,9 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
{
const char *this_name = file_data->file_names[j];

- if (FILENAME_CMP (name, this_name) == 0)
+ if (FILENAME_CMP (name, this_name) == 0
+ || (!is_abs && compare_filenames_for_search (this_name,
+ name, name_len)))
{
if (dw2_map_expand_apply (objfile, per_cu,
name, full_path, real_path,
@@ -2434,9 +2436,12 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
return 1;
}

- if (check_basename && ! base_cu
- && FILENAME_CMP (lbasename (this_name), name) == 0)
- base_cu = per_cu;
+ {
+ if (dw2_map_expand_apply (objfile, per_cu,
+ name, full_path, real_path,
+ callback, data))
+ return 1;
+ }

/* Before we invoke realpath, which can get expensive when many
files are involved, do a quick comparison of the basenames. */
@@ -2450,7 +2455,10 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
file_data, j);

if (this_real_name != NULL
- && FILENAME_CMP (full_path, this_real_name) == 0)
+ && (FILENAME_CMP (full_path, this_real_name) == 0
+ || (!is_abs
+ && compare_filenames_for_search (this_real_name,
+ name, name_len))))
{
if (dw2_map_expand_apply (objfile, per_cu,
name, full_path, real_path,
@@ -2465,7 +2473,10 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
file_data, j);

if (this_real_name != NULL
- && FILENAME_CMP (real_path, this_real_name) == 0)
+ && (FILENAME_CMP (real_path, this_real_name) == 0
+ || (!is_abs
+ && compare_filenames_for_search (this_real_name,
+ name, name_len))))
{
if (dw2_map_expand_apply (objfile, per_cu,
name, full_path, real_path,
@@ -2476,14 +2487,6 @@ dw2_map_symtabs_matching_filename (struct objfile *objfile, const char *name,
}
}

- if (base_cu)
- {
- if (dw2_map_expand_apply (objfile, base_cu,
- name, full_path, real_path,
- callback, data))
- return 1;
- }
-
return 0;
}

diff --git a/gdb/psymtab.c b/gdb/psymtab.c
index 1f9457f..998bc05 100644
--- a/gdb/psymtab.c
+++ b/gdb/psymtab.c
@@ -164,10 +164,14 @@ partial_map_symtabs_matching_filename (struct objfile *objfile,
{
struct partial_symtab *pst;
const char *name_basename = lbasename (name);
+ int name_len = strlen (name);
+ int is_abs = IS_ABSOLUTE_PATH (name);

ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, pst)
{
- if (FILENAME_CMP (name, pst->filename) == 0)
+ if (FILENAME_CMP (name, pst->filename) == 0
+ || (!is_abs && compare_filenames_for_search (pst->filename,
+ name, name_len)))
{
if (partial_map_expand_apply (objfile, name, full_path, real_path,
pst, callback, data))
@@ -186,7 +190,9 @@ partial_map_symtabs_matching_filename (struct objfile *objfile,
{
psymtab_to_fullname (pst);
if (pst->fullname != NULL
- && FILENAME_CMP (full_path, pst->fullname) == 0)
+ && (FILENAME_CMP (full_path, pst->fullname) == 0
+ || (!is_abs && compare_filenames_for_search (pst->fullname,
+ name, name_len))))
{
if (partial_map_expand_apply (objfile, name, full_path, real_path,
pst, callback, data))
@@ -203,7 +209,10 @@ partial_map_symtabs_matching_filename (struct objfile *objfile,
rp = gdb_realpath (pst->fullname);
make_cleanup (xfree, rp);
}
- if (rp != NULL && FILENAME_CMP (real_path, rp) == 0)
+ if (rp != NULL
+ && (FILENAME_CMP (real_path, rp) == 0
+ || (!is_abs && compare_filenames_for_search (real_path,
+ name, name_len))))
{
if (partial_map_expand_apply (objfile, name, full_path, real_path,
pst, callback, data))
@@ -212,17 +221,6 @@ partial_map_symtabs_matching_filename (struct objfile *objfile,
}
}

- /* Now, search for a matching tail (only if name doesn't have any dirs). */
-
- if (name_basename == name)
- ALL_OBJFILE_PSYMTABS_REQUIRED (objfile, pst)
- {
- if (FILENAME_CMP (lbasename (pst->filename), name) == 0)
- if (partial_map_expand_apply (objfile, name, full_path, real_path, pst,
- callback, data))
- return 1;
- }
-
return 0;
}

diff --git a/gdb/symfile.h b/gdb/symfile.h
index 6d39162..6b664cd 100644
--- a/gdb/symfile.h
+++ b/gdb/symfile.h
@@ -153,7 +153,7 @@ struct quick_symbol_functions
/* Expand and iterate over each "partial" symbol table in OBJFILE
where the source file is named NAME.

- If there is no '/' in the name, a match after a '/' in the symbol
+ If NAME is not absolute, a match after a '/' in the symbol
table's file name will also work. FULL_PATH is the absolute file
name, and REAL_PATH is the same, run through gdb_realpath.

diff --git a/gdb/symtab.c b/gdb/symtab.c
index ab6a1aa..9471b81 100644
--- a/gdb/symtab.c
+++ b/gdb/symtab.c
@@ -145,6 +145,32 @@ multiple_symbols_select_mode (void)

const struct block *block_found;

+/* See whether FILENAME matches SEARCH_NAME using the rule that we
+ advertise to the user. SEARCH_LEN is the length of SEARCH_NAME.
+ We assume that SEARCH_NAME is a relative path. Returns true if
+ they match, false otherwise. */
+
+int
+compare_filenames_for_search (const char *filename, const char *search_name,
+ int search_len)
+{
+ int len = strlen (filename);
+ int offset;
+
+ if (len < search_len)
+ return 0;
+
+ /* The tail of FILENAME must match. */
+ if (FILENAME_CMP (filename + len - search_len, search_name) != 0)
+ return 0;
+
+ /* Either the names must completely match, or the character
+ preceding the trailing SEARCH_NAME segment of FILENAME must be a
+ directory separator. */
+ return (len == search_len
+ || IS_DIR_SEPARATOR (filename[len - search_len - 1]));
+}
+
/* Check for a symtab of a specific name by searching some symtabs.
This is a helper function for callbacks of iterate_over_symtabs.

@@ -169,15 +195,24 @@ iterate_over_some_symtabs (const char *name,
struct symtab *s = NULL;
struct cleanup *cleanup;
const char* base_name = lbasename (name);
+ int name_len = strlen (name);
+ int is_abs = IS_ABSOLUTE_PATH (name);

for (s = first; s != NULL && s != after_last; s = s->next)
{
+ /* Exact match is always ok. */
if (FILENAME_CMP (name, s->filename) == 0)
{
if (callback (s, data))
return 1;
}

+ if (!is_abs && compare_filenames_for_search (s->filename, name, name_len))
+ {
+ if (callback (s, data))
+ return 1;
+ }
+
/* Before we invoke realpath, which can get expensive when many
files are involved, do a quick comparison of the basenames. */
if (! basenames_may_differ
@@ -196,6 +231,13 @@ iterate_over_some_symtabs (const char *name,
if (callback (s, data))
return 1;
}
+
+ if (fp != NULL && !is_abs && compare_filenames_for_search (fp, name,
+ name_len))
+ {
+ if (callback (s, data))
+ return 1;
+ }
}

if (real_path != NULL)
@@ -212,24 +254,16 @@ iterate_over_some_symtabs (const char *name,
if (callback (s, data))
return 1;
}
+
+ if (!is_abs && compare_filenames_for_search (rp, name, name_len))
+ {
+ if (callback (s, data))
+ return 1;
+ }
}
}
}

- /* Now, search for a matching tail (only if name doesn't have any dirs). */
-
- if (lbasename (name) == name)
- {
- for (s = first; s != NULL && s != after_last; s = s->next)
- {
- if (FILENAME_CMP (lbasename (s->filename), name) == 0)
- {
- if (callback (s, data))
- return 1;
- }
- }
- }
-
return 0;
}

diff --git a/gdb/symtab.h b/gdb/symtab.h
index 7b96ab3..4836dd6 100644
--- a/gdb/symtab.h
+++ b/gdb/symtab.h
@@ -1239,6 +1239,10 @@ struct objfile *lookup_objfile_from_block (const struct block *block);

extern int basenames_may_differ;

+int compare_filenames_for_search (const char *filename,
+ const char *search_name,
+ int search_len);
+
int iterate_over_some_symtabs (const char *name,
const char *full_path,
const char *real_path,
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index 4ac2fd8..02f796a 100644
--- a/gdb/testsuite/gdb.linespec/linespec.exp
+++ b/gdb/testsuite/gdb.linespec/linespec.exp
@@ -45,18 +45,19 @@ if {$l1 != $l2} {
}

gdb_test "break thefile.cc:$l1" \
- "Breakpoint 1 at $hex: thefile.cc:$l1. \[(\]2 locations\[)\]" \
+ "Breakpoint $decimal at $hex: thefile.cc:$l1. \[(\]2 locations\[)\]" \
"multi-location break using file:line"

-# We'd like this to work, but it currently does not.
-# gdb_test "break one/thefile.cc:$l1"
+gdb_test "break one/thefile.cc:$l1" \
+ "Breakpoint $decimal at $hex: file .*thefile.cc, line $l1." \
+ "single-location break using dir/file:line"

gdb_test "break dupname" \
- "Breakpoint 2 at $hex: dupname. \[(\]2 locations\[)\]" \
+ "Breakpoint $decimal at $hex: dupname. \[(\]2 locations\[)\]" \
"multi-location break using duplicate function name"

gdb_test "break dupname:label" \
- "Breakpoint 3 at $hex: dupname:label. \[(\]2 locations\[)\]" \
+ "Breakpoint $decimal at $hex: dupname:label. \[(\]2 locations\[)\]" \
"multi-location break using duplicate function name and label"

gdb_test_no_output "set breakpoint pending off" \