Re: [PATCH v8] gdb: Add source-tracking breakpoints feature

Andrew Burgess <[email protected]>
Newsgroups gmane.comp.gdb.patches
Message-ID <[email protected]>
Hi Alexandra,

I took a look through this and have a small number of very minor nits
left to clean up.  Thanks for continuing to work on this project.

Alexandra Hájková <[email protected]> writes:

> When we rerun the executable after changing its source files,
> GDB would re-set all previously set breakpoints. The
> breakpoints set to the function names would remain at their initial
> locations. But the breakpoints which used filename:line notation would
> be silently shifted following the source code changes.
>
> To address this, GDB now optionally captures a small window of source
> code lines around each breakpoint set with filename:line notation,
> when it is first set. When the binary is reloaded, GDB detects the BFD
> change and tries to locate the same source context in the new file,
> and if successful, re-sets the breakpoint to the matched source code
> line.
>
> The breakpoint_source structure stores captured source code lines
> around a breakpoint location, along with a reference to the BFD
> that was current when the source was captured.
>
> When source tracking is enabled (via 'set breakpoint source-tracking
> enabled on'), GDB captures 3 lines of source context
> (BREAKPOINT_SRC_CTX_LINES) along with the current BFD when a
> breakpoint is first set.  On executable reload (detected by comparing
> BFDs), it searches within a 12-line window
> (BREAKPOINT_SRC_CTX_LINES * BREAKPOINT_SRC_SEARCH_MULTIPLIER) for
> the best match and adjusts the breakpoint location if needed.
>
> If source tracking is disabled after breakpoints have been tracked,
> all existing source tracking information is discarded and a message
> is printed.
>
> Tests added:
>   gdb.base/adjust_breakpoint.exp
>   gdb.base/adjust_breakpoint-missing-source.exp
>   gdb.base/source-tracking-inline.exp
>   gdb.base/test_source_tracking.exp
>
> adjust_breakpoint.exp covers four scenarios:
>   - adjust the breakpoint when lines are deleted
>   - adjust the breakpoint when lines are inserted
>   - the tracked line disappears entirely
>   - verify the tracking can be disabled
>
> adjust_breakpoint-missing-source.exp covers the edge case where source
> files are unavailable, verifying GDB falls back to non-tracking breakpoints.
>
> source-tracking-inline.exp covers source tracking with inline functions.

You list 4 new tests and then describe 3 of them.  Adding a sentence for
the new test would be great:

  test_source_tracking.exp verifies that source context is
  correctly captured when the breakpoint is on the last line
  of the file.

>
> Add maintenance command to print tracked source code.
> Add documentation for the new source-tracking breakpoints feature.
>
> Limitations of the current implementation:
>
> Source tracking is not enabled for pending breakpoints that become
> non-pending.  When a breakpoint is created pending (e.g. with 'set
> breakpoint pending on'), source context is not captured at creation
> time since no symtab is available yet.  When the breakpoint later
> resolves to a location, re_set_default() only updates existing tracked
> breakpoints and does not initiate tracking for newly resolved ones.
> This could be fixed in the future by initiating source tracking in
> re_set_default() when a breakpoint transitions from pending to
> non-pending.
>
> Source tracking for ranged breakpoints is not currently supported.
> Ranged breakpoints have a start and end location spec, and tracking
> both independently raises questions about whether to preserve the
> range length or track each end separately.  For now, ranged
> breakpoints will never be source-tracked.
>
> Reviewed-By: Eli Zaretskii <[email protected]>
> ---
> v8:
> - Fix off-by-one error in breakpoint_source_capture and
> add gdb.base/test_source_tracking.exp to test this corner case
> - Fix API drift: symtab::compunit() now returns compunit_symtab & 
> - Fix locspec assignment ordering in adjust_bp_for_source_tracking
> - Move the breakpoint_source struct definition from breakpoint.h to breakpoint.c
>
>  gdb/NEWS                                      |  14 +
>  gdb/breakpoint.c                              | 487 ++++++++++++++++++
>  gdb/breakpoint.h                              |  16 +-
>  gdb/doc/gdb.texinfo                           |  45 ++
>  .../gdb.base/adjust_breakpoint-2.cpp          |  39 ++
>  .../gdb.base/adjust_breakpoint-3.cpp          |  41 ++
>  .../gdb.base/adjust_breakpoint-4.cpp          |  37 ++
>  .../adjust_breakpoint-missing-source.exp      |  55 ++
>  gdb/testsuite/gdb.base/adjust_breakpoint.cpp  |  40 ++
>  gdb/testsuite/gdb.base/adjust_breakpoint.exp  | 168 ++++++
>  .../gdb.base/source-tracking-inline-1.c       |  50 ++
>  .../gdb.base/source-tracking-inline-2.c       |  49 ++
>  .../gdb.base/source-tracking-inline.exp       |  80 +++
>  gdb/testsuite/gdb.base/test_source_tracking.c |  18 +
>  .../gdb.base/test_source_tracking.exp         |  43 ++
>  15 files changed, 1181 insertions(+), 1 deletion(-)
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint-2.cpp
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint-3.cpp
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint-4.cpp
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint-missing-source.exp
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint.cpp
>  create mode 100644 gdb/testsuite/gdb.base/adjust_breakpoint.exp
>  create mode 100644 gdb/testsuite/gdb.base/source-tracking-inline-1.c
>  create mode 100644 gdb/testsuite/gdb.base/source-tracking-inline-2.c
>  create mode 100644 gdb/testsuite/gdb.base/source-tracking-inline.exp
>  create mode 100644 gdb/testsuite/gdb.base/test_source_tracking.c
>  create mode 100644 gdb/testsuite/gdb.base/test_source_tracking.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ec9b5a33787..553d8c49ff6 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -6,6 +6,13 @@
>  * Support for the Common Trace Format (CTF) has been removed.  GDB now
>    saves trace information exclusively in its own "tfile" format.
>  
> +* GDB now supports source-tracking breakpoints, which automatically
> +  adjust their location when source code changes between rebuilds.
> +  When enabled, file and line breakpoints capture the surrounding
> +  source code context and use it to adjust the breakpoint line if the
> +  source is modified.  Source tracking can be enabled with 'set
> +  breakpoint source-tracking enabled on'.
> +
>  * Support for .gdb_index sections with version less than 7 has been
>    removed.
>  
> @@ -141,6 +148,13 @@ unset local-environment
>    environment.  The local environment is used by "shell", "pipe", and
>    other commands that launch a subprocess other than an inferior.
>  
> +set breakpoint source-tracking enabled [on|off]
> +show breakpoint source-tracking enabled
> +  Enable or disable source-tracking for file and line breakpoints.
> +  When enabled, breakpoints capture surrounding source code and
> +  automatically adjust their location when the source changes between
> +  recompilations.
> +

The following new command needs adding to the NEWS file:

 maint info source-tracking-context
   ...

>  save history FILENAME
>    Save the command history to the given file.
>  
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index e4df4df04a7..0d116a217cf 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c

> +
> +/* Print captured source lines to stdout, marking the breakpoint line with '>'.  */
> +
> +static void
> +breakpoint_source_print (const breakpoint_source *src)
> +{
> +  if (!breakpoint_source_is_tracked (src))
> +    return;
> +
> +  int start_line = breakpoint_source_get_start_line (src);
> +  for (std::size_t j = 0; j < src->source_lines.size (); j++)
> +    {
> +      int line_num = start_line + (int) j;
> +      char prefix;
> +      if (j == src->bp_line_stored)
> +	prefix = '>';
> +      else
> +	prefix = ' ';
> +      gdb_printf ("%c %ps %s", prefix,
> +		  styled_string (line_number_style.style (),
> +				 pulongest (line_num)),

As LINE_NUM is signed, `plongest` would be a better choice here.
There's an unfortunate mix of calls throughout GDB with some using
pulongest and some plongest.  But if we consider the
ui_out::field_signed calls, I think plongest is the most common, and
feels like the correct choice for signed values.

> +		  src->source_lines[j].c_str ());
> +      if (src->source_lines[j].empty ()
> +	  || src->source_lines[j].back () != '\n')
> +	gdb_putc ('\n');
> +    }
> +}
> +
> +/* Implement the "maintenance info source-tracking-context" command.  */
> +
> +static void
> +maintenance_info_source_tracking_context (const char *args, int from_tty)
> +{
> +  if (args == nullptr || *args == '\0')
> +    error (_("Breakpoint number required."));
> +
> +  /* Parse the breakpoint number.  */
> +  const char *end = args;
> +  int num = get_number_trailer (&end, 0);
> +
> +  if (num <= 0)
> +    error (_("Invalid breakpoint number '%s'."), args);
> +
> +  /* Find the breakpoint.  */
> +  breakpoint *b = nullptr;
> +  for (breakpoint &bp : all_breakpoints ())
> +    {
> +      if (bp.number == num)
> +	{
> +	  b = &bp;
> +	  break;
> +	}
> +    }
> +
> +  if (b == nullptr)
> +    error (_("No breakpoint number %d."), num);
> +
> +  /* Check if source tracking is enabled for this breakpoint.  */
> +  if (!breakpoint_source_is_tracked (b->bp_source.get ()))
> +    {
> +      gdb_printf (_("Breakpoint %d does not have source tracking enabled.\n"), num);
> +      return;

The documentation for this command says:

     For source-tracking breakpoints (*note Breakpoints::), print the
     tracked source code context for breakpoint NUM.  If breakpoint NUM
     is not source tracked, or NUM is not a valid breakpoint number,
     then the command gives an error.

Except for non-tracked breakpoints you don't give an error, you print a
message and return.  You should either update the documentation to
match the implementation, or the implementation to match the docs, I
think either choice would be fine.  I've left a comment below where the
docs are inconsistent.

> @@ -8926,6 +9204,32 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
>  				enabled, flags,
>  				display_canonical);
>  
> +  /* Only capture source lines for file:line breakpoints when source
> +     tracking is enabled.  We check explicit_line to ensure the user
> +     explicitly specified a line number (e.g., "break file.c:23" or
> +     "break 23"), as opposed to "break function_name" or temporary
> +     breakpoints set by commands like "start".
> +
> +     We also only track single-location breakpoints.  Multi-location
> +     breakpoints (e.g., breakpoints on inline functions that are inlined
> +     in multiple places) are too complex to track reliably as each location
> +     may have moved differently.  */
> +  if (source_tracking_breakpoints && sals.size () == 1
> +      && sals[0].explicit_line
> +      && breakpoint_locspec_suitable_for_tracking (b->locspec.get ()))
> +    {
> +      /* Capture source if we have valid symtab and line info.
> +	 This works for both "b file:line" and "b line" formats.
> +	 We capture BREAKPOINT_SRC_CTX_LINES lines to provide
> +	 context around the breakpoint location.  */
> +      b->bp_source = std::make_unique<breakpoint_source>
> +	(breakpoint_source_capture (sals, BREAKPOINT_SRC_CTX_LINES));
> +
> +      if (!breakpoint_source_is_tracked (b->bp_source.get ()))
> +	warning (_("Source file not available; breakpoint will not be "
> +		   "source-tracked."));

Optional: you could change this to:

      if (!breakpoint_source_is_tracked (b->bp_source.get ()))
	{
	  warning (_("Source file not available; breakpoint will not be "
		     "source-tracked."));
	  b->bp_source.reset ();
	}

this would discard the empty breakpoint_source object, saving a small
amount of memory.  Not discarding it is harmless, it just uses a little
memory, but it's not a leak, and an empty breakpoint_source still
returns false for breakpoint_source_is_tracked, so not deleting is
functionally correct, this is really up to you.

> +    }
> +
>    install_breakpoint (internal, std::move (b), 0);
>  }
>  
> @@ -13183,6 +13487,154 @@ code_breakpoint::location_spec_to_sals (location_spec *locspec,
>    return sals;
>  }
>  
> +/* Match BREAKPOINT_SRC_CTX_LINES lines of the initially stored source in a
> +   BREAKPOINT_SRC_CTX_LINES * BREAKPOINT_SRC_SEARCH_MULTIPLIER lines current
> +   source window.
> +
> +   Returns new breakpoint line on success or -1 on failure.  */
> +
> +static int
> +sliding_window_match (breakpoint_source *bp_source,
> +		      breakpoint_source *tmp_source)
> +{
> +  /* The index into BP_SOURCE's lines where the breakpoint was placed.  */
> +  size_t bp_stored = bp_source->bp_line_stored;
> +  size_t bp_size = bp_source->source_lines.size ();
> +
> +  /* An empty string, used if the breakpoint line is at the start or end of
> +     the context window.  */
> +  static std::string empty_string;

This could also be const, so `static const std::string emptry_string`.

> +
> +/* See breakpoint.h.  */
> +
> +void
> +code_breakpoint::adjust_bp_for_source_tracking
> +  (program_space *filter_pspace,
> +   std::vector<symtab_and_line> &expanded)
> +{
> +  if (expanded.empty () || expanded[0].symtab == nullptr
> +      || !breakpoint_source_is_tracked (bp_source.get ()))
> +    return;
> +
> +  struct compunit_symtab &cust = expanded[0].symtab->compunit ();
> +  if (cust.objfile () == nullptr)
> +    return;
> +
> +  bfd *current_bfd = cust.objfile ()->obfd.get ();
> +  if (bp_source->source_bfd.get () == current_bfd)
> +    return;
> +
> +  /* BFD changed — executable was reloaded.  */

This is a non ASCII dash character.  Can you replace it with a standard
ASCII dash please.  Ideally comments should be complete sentences, so
rewriting to remove the need for a dash would be best.

> +  if (expanded.size () != 1)
> +    {
> +      warning (_("Breakpoint %d now has multiple locations after reload, "
> +		 "disabling source tracking."), number);
> +      bp_source.reset ();
> +      return;
> +    }
> +
> +  /* If this fails then the location spec has changed since the
> +     breakpoint's source tracking was initially setup.  */
> +  gdb_assert (breakpoint_locspec_suitable_for_tracking (locspec.get ()));
> +
> +  std::string line;
> +  auto restore_styling = make_scoped_restore (&source_styling, false);
> +  if (!g_source_cache.get_source_lines (expanded[0].symtab,
> +					expanded[0].line,
> +					expanded[0].line, &line))
> +    {
> +      /* Source is unreadable after reload — drop tracking.  */

Non ASCII dash again.

> +      bp_source.reset ();
> +      return;
> +    }
> +
> +  if (line == bp_source->source_lines[bp_source->bp_line_stored])
> +    {
> +      /* Line unchanged — just refresh the capture with the new BFD.  */

Non ASCII dash again.

> +      bp_source = std::make_unique<breakpoint_source>
> +	(breakpoint_source_capture (expanded, BREAKPOINT_SRC_CTX_LINES));
> +      return;
> +    }
> +
> +  breakpoint_source tmp_source
> +    = breakpoint_source_capture (expanded,
> +				 BREAKPOINT_SRC_CTX_LINES
> +				 * BREAKPOINT_SRC_SEARCH_MULTIPLIER);
> +  int new_bp_line = sliding_window_match (bp_source.get (), &tmp_source);
> +  if (new_bp_line == -1)
> +    {
> +      warning (_("Breakpoint %d source code not found "
> +		 "after reload, keeping original location."), number);
> +      bp_source.reset ();
> +      return;
> +    }
> +
> +  auto *explicit_loc = as_explicit_location_spec (locspec.get ());
> +  location_spec_up new_locspec = explicit_loc->clone ();
> +  auto *new_explicit = as_explicit_location_spec (new_locspec.get ());
> +  new_explicit->line_offset.offset = new_bp_line;
> +  new_explicit->line_offset.sign = LINE_OFFSET_NONE;
> +  new_explicit->set_string ("");   /* Invalidate the cached display string.  */

Sorry to be a pain, but GDB style is to place comments before the source
line, not trailing at the end.  Can you move this comment to a line of
its own please.

> +
> +  int found;
> +  expanded = location_spec_to_sals (new_locspec.get (), filter_pspace, &found);
> +  if (!found)
> +    {
> +      warning (_("Breakpoint %d adjusted to line %d but location could not "
> +                 "be resolved; keeping original location."), number, new_bp_line);

This line needs indenting with tabs.  If you use 'git check' it should
point out these white space issues for you.

> diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
> index 722d75390fa..2698fc4d441 100644
> --- a/gdb/breakpoint.h
> +++ b/gdb/breakpoint.h
> @@ -27,6 +27,7 @@
>  #include "probe.h"
>  #include "location.h"
>  #include <vector>
> +#include <memory>
>  #include "gdbsupport/array-view.h"
>  #include "gdbsupport/filtered-iterator.h"
>  #include "gdbsupport/iterator-range.h"
> @@ -34,7 +35,6 @@
>  #include "gdbsupport/safe-iterator.h"
>  #include "cli/cli-script.h"
>  #include "target/waitstatus.h"
> -

Unnecessary white space change, please revert.

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index a698b2b8451..d304f885def 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -4661,6 +4661,28 @@ program.
>  On some systems, you can set breakpoints in shared libraries before
>  the executable is run.
>  
> +@cindex source-tracking breakpoints
> +@cindex breakpoints, automatic adjustment when source changes
> +@value{GDBN} supports @dfn{source-tracking breakpoints}, which
> +automatically adjust their location when source code changes between
> +recompilations.  When enabled with @code{set breakpoint source-tracking
> +enabled on}, breakpoints set by file and line number capture a small
> +window of surrounding source lines: the line immediately before the
> +breakpoint, the breakpoint line itself, and the line immediately after
> +it.  If the source file is modified and the executable is rebuilt,
> +@value{GDBN} searches a window of approximately 12 lines centered on the
> +breakpoint's original position for a match.  A candidate line is
> +accepted when it matches the captured breakpoint line and both of
> +its immediate neighbors also match, reducing false positives from
> +short or repeated lines.  @value{GDBN} uses the first such confirmed
> +match found, scanning from the top of the search window.  If the same
> +code sequence appears more than once within the search window, the
> +earliest occurrence is chosen; code outside the search window is not
> +considered.  If no match is found, @value{GDBN} issues a warning and
> +keeps the breakpoint at its original location, disabling source tracking
> +for that breakpoint.  Note that breakpoints set by function name or
> +address are not affected by source tracking.  @xref{Set Breaks}.
> +
>  @cindex watchpoints
>  @cindex data breakpoints
>  @cindex memory tracing
> @@ -42017,6 +42039,13 @@ Shared library events.
>  
>  @end table
>  
> +@kindex maint info source-tracking-context
> +@item maint info source-tracking-context @var{num}
> +For source-tracking breakpoints (@pxref{Breakpoints}), print the
> +tracked source code context for breakpoint @var{num}.  If breakpoint
> +@var{num} is not source tracked, or @var{num} is not a valid
> +breakpoint number, then the command gives an error.

Here is the documentation inconsistency that I mentioned earlier.
Currently GDB doesn't give an error for non-source tracked breakpoints,
it just prints a message and returns.

> diff --git a/gdb/testsuite/gdb.base/adjust_breakpoint.exp b/gdb/testsuite/gdb.base/adjust_breakpoint.exp
> new file mode 100644
> index 00000000000..123c03d322d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/adjust_breakpoint.exp
> @@ -0,0 +1,168 @@
> +# Copyright 2026 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# 1) Set the breakpoint to a certain line in $srcfile. Replace the $srcfile
> +# with tmp-$srcfile which is exactly the same except one line is missing,
> +# which changes the line where the breakpoint was initially set and moves
> +# the breakpoint one line backwards.
> +# Check if GDB adjusted the line correctly.
> +#
> +# 2) Do all the same but move the breakpoint a few lines forward by adding an
> +# additional line to the tmp2-$srcfile.
> +
> +standard_testfile .cpp -2.cpp -3.cpp -4.cpp
> +set build_srcfile ${testfile}-xxx.cpp
> +
> +set new_source_file [standard_output_file ${build_srcfile}]
> +remote_exec build "cp ${srcdir}/${subdir}/${srcfile} $new_source_file"
> +if { [prepare_for_testing "failed to prepare" $testfile $new_source_file] } {
> +    return
> +}
> +
> +# Enable source tracking for breakpoints.
> +gdb_test_no_output "set breakpoint source-tracking enabled on" \
> +    "enable source tracking breakpoints"
> +
> +# If a breakpoint is being source tracked, then turning source
> +# tracking off discards the tracking information and prints a message.
> +# If no breakpoints are being source tracked then disabling source
> +# tracking should be silent.
> +with_test_prefix "check disabling is silent" {
> +    gdb_test_no_output "set breakpoint source-tracking enabled off" \
> +	"disable"
> +
> +    gdb_test_no_output "set breakpoint source-tracking enabled on" \
> +	"enable"
> +}
> +
> +# Test that the breakpoint can be adjusted backward.
> +set lineno [gdb_get_line_number "var += i;" $new_source_file]
> +gdb_breakpoint ${build_srcfile}:$lineno
> +
> +# Sleep to ensure timestamp changes when we rebuild.
> +sleep 1
> +remote_exec build "cp ${srcdir}/${subdir}/${srcfile2} $new_source_file"
> +if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
> +    return
> +}
> +
> +set lineno [expr {$lineno - 1}]
> +gdb_test "run" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
> +    "run stops at adjusted breakpoint location"
> +gdb_test "info breakpoints" \
> +    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time" \
> +    "info breakpoints show the breakpoint was adjusted one line backward"
> +
> +# Test that the breakpoint can be adjusted forward.
> +clean_restart ${testfile}
> +gdb_test_no_output "set breakpoint source-tracking enabled on" \
> +    "enable source tracking breakpoints for part 2"
> +gdb_breakpoint ${build_srcfile}:$lineno
> +
> +# Sleep to ensure timestamp changes when we rebuild.
> +sleep 1
> +remote_exec build "cp ${srcdir}/${subdir}/${srcfile3} $new_source_file"
> +if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
> +    return
> +}
> +
> +set lineno [expr {$lineno + 2}]
> +gdb_test "run" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
> +    "run for the second time stops at adjusted breakpoint location"
> +gdb_test "info breakpoints" \
> +    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time" \
> +    "info breakpoints show the breakpoint was adjusted forward"
> +
> +# Disable source tracking breakpoints, the existing tracking
> +# information is discarded.
> +gdb_test "set breakpoint source-tracking enabled off" \
> +    "^Discarding existing source tracking information\\." \
> +    "disable source tracking, existing tracking is discarded"
> +
> +gdb_test "info breakpoints" \
> +    [multi_line \
> +	 "1\\s+breakpoint\\s+keep\\s+y\[^\r\n\]+" \
> +	 "\\s+breakpoint already hit \[^\r\n\]+"] \
> +    "info breakpoints breakpoint no longer tracked"
> +
> +# Test what happens when the breakpoint line disappears.
> +clean_restart ${testfile}
> +gdb_test_no_output "set breakpoint source-tracking enabled on" \
> +    "enable source tracking breakpoints for part 3"
> +set lineno [gdb_get_line_number "var += 10;" $new_source_file]
> +gdb_breakpoint ${build_srcfile}:$lineno
> +
> +# Sleep to ensure timestamp changes when we rebuild.
> +sleep 1
> +remote_exec build "cp ${srcdir}/${subdir}/${srcfile4} $new_source_file"
> +if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
> +    return
> +}
> +
> +# When the original line is removed and cannot be found in the search window,
> +# the breakpoint stays at the symbol-resolved location. Line 10 becomes blank
> +# in tmp3, so GDB resolves it to line 11 (return var;) or stays at line 10.
> +# We test that it doesn't move beyond the reasonable range.
> +set lineno_re "(?:$lineno|[expr {$lineno + 1}])"
> +gdb_test "run" "Breakpoint 1,.*$build_srcfile:$lineno_re\r\n$lineno_re\t.*" \
> +    "run for the third time stops near original location"
> +gdb_test "info breakpoints" \
> +    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno_re.*already hit 1 time" \
> +    "the breakpoint stays near original location when line disappears"
> +
> +# Test that with source tracking disabled the breakpoint should not be
> +# adjusted.
> +clean_restart ${testfile}
> +# Don't enable source tracking - test that breakpoints don't adjust without it
> +set lineno [gdb_get_line_number "return var;" $new_source_file]
> +gdb_breakpoint ${build_srcfile}:$lineno
> +
> +# Sleep to ensure timestamp changes when we rebuild.
> +sleep 1
> +remote_exec build "cp ${srcdir}/${subdir}/${srcfile2} $new_source_file"
> +if {[build_executable "failed to prepare" $testfile $new_source_file] == -1} {
> +    return
> +}
> +
> +gdb_test "run" "Breakpoint 1,.*$build_srcfile:$lineno\r\n$lineno\t.*" \
> +    "run for the fourth time stops at unadjusted location"
> +gdb_test "info breakpoints" \
> +    "breakpoint.*keep.*y.*$hex.*$build_srcfile:$lineno.*already hit 1 time" \
> +    "breakpoint not adjusted when tracking disabled"
> +
> +# Test that relative line breakpoints (e.g., "b +1") with source tracking
> +# enabled do not produce a spurious warning.  Relative line offsets are not
> +# supported for source tracking, so the breakpoint should be created silently
> +# without any warning message.
> +clean_restart ${testfile}
> +gdb_test_no_output "set breakpoint source-tracking enabled on" \
> +    "enable source tracking for relative line test"
> +
> +# Run to main so we have a current line context for relative breakpoints.
> +if {![runto_main]} {
> +    return
> +}
> +
> +# "b +1" is a relative offset breakpoint — not trackable, but should be

Non ASCII dash again.

> +# created silently with no warning.
> +gdb_test_multiple "break +1" "relative line breakpoint creates no warning" {
> +    -re -wrap "warning:.*source-track.*" {
> +	fail "$gdb_test_name (unexpected warning)"
> +    }
> +    -re -wrap "Breakpoint $decimal at $hex.*" {
> +	pass $gdb_test_name
> +    }
> +}
> +

Blank line at end of file, please remove.

> diff --git a/gdb/testsuite/gdb.base/test_source_tracking.exp b/gdb/testsuite/gdb.base/test_source_tracking.exp
> new file mode 100644
> index 00000000000..597447aaa73
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/test_source_tracking.exp
> @@ -0,0 +1,43 @@
> +# Copyright 2026 Free Software Foundation, Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program.  If not, see <http://www.gnu.org/licenses/>.
> +
> +# Test source-tracking correctly captures context when bp is on the last line.
> +# This covers an off-by-one in breakpoint_source_capture where the clipping
> +# formula excluded the last line of the file from the capture window.
> +
> +standard_testfile .c
> +
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } {
> +    return
> +}
> +
> +gdb_test_no_output "set breakpoint source-tracking enabled on"
> +
> +set lineno [gdb_get_line_number "BPLastLine" $srcfile]
> +gdb_breakpoint ${srcfile}:$lineno
> +
> +# The '>' marker should be on the actual breakpoint line, not the line before it.
> +# Use gdb_test_multiple so the pattern can match the '>' line anywhere in the
> +# multi-line output (gdb_test's -wrap prevents matching across newlines).
> +gdb_test_multiple "maint info source-tracking-context 1" \
> +    "breakpoint line is correctly marked in captured context" {
> +    -re "> \[0-9\]+ \[^\r\n\]*BPLastLine\[^\r\n\]*\r\n$gdb_prompt $" {
> +	pass $gdb_test_name
> +    }
> +    -re "$gdb_prompt $" {
> +	fail $gdb_test_name
> +    }
> +}
> +

Blank line at end of the file, please remove.

Thanks,
Andrew
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.