From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id ITVJC0kcVmqDtAcAWB0awg (envelope-from ) for ; Tue, 14 Jul 2026 07:23:53 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ua5o5olt; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 1CA0B1E09E; Tue, 14 Jul 2026 07:23:53 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-6.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id E24541E099 for ; Tue, 14 Jul 2026 07:23:50 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DD27C4BA2E15 for ; Tue, 14 Jul 2026 11:23:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org DD27C4BA2E15 Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ua5o5olt Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 3A39F4BA2E08 for ; Tue, 14 Jul 2026 11:23:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3A39F4BA2E08 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 3A39F4BA2E08 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784028197; cv=none; b=GJzwPHLw1rClIMT8zwHyNWogM9IFWOiLaVFvxufjiC7R5Bc6XIB+3NKf6XRf3luvD/NBCdlVoj1wmia5UrVNYkSyozlLqCJBFREjWma2V9cjTYy+jrKCaM8qpBjClYN9hK6viKdER0krKEeHVGKcyJmCmoxzgtt2KDoLuZFxmqk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1784028197; c=relaxed/simple; bh=e2Cc7JQCT5FAjMUNc19+iSqSN9v2w0HVjnJCck9xBMs=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=rafNyrGTD0pbhxWDwkRQBPvVNFA2k/lbIQbX+ttzL7SqTk8GfuDdnoohu1IDkbEg08w8jip7v0P0Lq1GGcu1qcgGLxTaRrsB1yDy1yGK3Aioz152mMT6vTXiXvFNtU4O301QjtEaCb619amBFPj6qaySN1pqfDq/Mw/0ChI6rSE= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ua5o5olt DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3A39F4BA2E08 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1784028196; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QAowW4zP+DBVjxC7+aGu7NP9ZREgzxkpezlepyWDIE0=; b=Ua5o5oltuC7elx7CtS/Id55f8pUgSsuHi28SBemaeKS6gBBAasPN0HX6QddFVeVX10TIQM fkb2UZovMd6onyulUfAi6pCMu46uixUwwOgcIyxomjZj/a+3eQKwBsnG5HltTOn3Z5JUaY j7h/HK380TDuOyFtY7gDq3HzfZR1dc8= Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-561-rHBlkKoZOZ-0DiAE5SlY7w-1; Tue, 14 Jul 2026 07:23:15 -0400 X-MC-Unique: rHBlkKoZOZ-0DiAE5SlY7w-1 X-Mimecast-MFC-AGG-ID: rHBlkKoZOZ-0DiAE5SlY7w_1784028194 Received: by mail-wm1-f69.google.com with SMTP id 5b1f17b1804b1-493b0a5e636so39325505e9.3 for ; Tue, 14 Jul 2026 04:23:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784028194; x=1784632994; h=content-transfer-encoding:content-type:mime-version:message-id:date :references:in-reply-to:subject:cc:to:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=dSMV9oaC67Kx6sUFU/1XTBTKSWBwaSrwjcsD2dVUrOY=; b=R2SEY0XBIEIsSYcZx9+gkKwt5/U6kuQv+OHZh8CKhYLRn0eZSmOXI/bNVavy6NZVEU q/Yhsr1dVVTxs8a4dXuGncb2A96mZXlC7TEnWQmvdvncUfQ/5pYd61ppl/KVZ+uJx/Gy WasgmnSBVNkuvu4aqZiZJ2rIJqPBR8CeS+BHE01Sey9kkOFJECIdEmOq2Qbk9bftNvU2 vu98wqVVQwsWh712pBKtmyYfzc6irMV1doJe+xDeMpPYYq49ITv/m1ldFG2H/RabmlyP rc8GPL6uWSTl2WsHpSIzI/DdyqJwDey/pX54Sv+xxb+Y3Uf2dCAJspyO4tDcrdgpP8iY 4Prw== X-Forwarded-Encrypted: i=1; AHgh+Rr6nAJjJS+7gioMDrKyqV7fXt9tFLSAJoqLmcy9t1KQSjMWyTnSe9zbaQJ88J6nmRKYHE7eoZRkT3vD9g==@sourceware.org X-Gm-Message-State: AOJu0YyvokcHh5/EAlSZpjVMYH4ThLqm2EY3NoOT8EgmkEP+2HkXZNht D1a1geN6sMA+FfYfQsm/W6LB2cDkkYjQpRrmUmyQME/kozGQ96uUXweAYCFPUcNiLSPh3UmfGZ0 HcxGbPxGaqAlgPnyfcfjlGjp2B2eU4pxprwDfov7Sa8o6jVn6RM61kn1Evcw5tMPOlM3sqsY= X-Gm-Gg: AfdE7ckqzZyUvJUkhGHlBWaF8+Nq3M9uA7Lqe+GQPtFFAF307oMrgS0fpLQ/rEPjsqN CjBc4/5atuKqtc54E2hWHcU5jt4hKdvZOljMSwv5UTdQNOsC3qP7m1vd/uAm/7JvY9DhX6pdZTr 5I2XcTnkIL8CPxSACrN6gqex23+SF4n2knZ3KRmbTsspLyQkLt49RnvrsDbuFILQkfBQ9IM8g7m VCQdV+6UrSodC2xSqyI8qkN1FDplrjz8gu4bIevzp+QXpSeFwLzV3WLm04qsIGWzsLJzwkBWMqi bVAAOtBTrdAKAT7/kp7Cu7UbhkMMcbf/r6T3Uu3z1U2xGqVc3YmTnzsGYM+oCzjeGmMYWUl+dZr Fg5Huk5g= X-Received: by 2002:a05:600c:47d0:b0:493:f140:c3fb with SMTP id 5b1f17b1804b1-495389ce01emr11187175e9.7.1784028193591; Tue, 14 Jul 2026 04:23:13 -0700 (PDT) X-Received: by 2002:a05:600c:47d0:b0:493:f140:c3fb with SMTP id 5b1f17b1804b1-495389ce01emr11186745e9.7.1784028192707; Tue, 14 Jul 2026 04:23:12 -0700 (PDT) Received: from localhost ([31.111.209.233]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-495087384casm68715225e9.8.2026.07.14.04.23.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 Jul 2026 04:23:12 -0700 (PDT) From: Andrew Burgess To: Alexandra =?utf-8?B?SMOhamtvdsOh?= , gdb-patches@sourceware.org Cc: ahajkova@redhat.com Subject: Re: [PATCH v8] gdb: Add source-tracking breakpoints feature In-Reply-To: <20260714094241.178097-1-ahajkova@redhat.com> References: <20260714094241.178097-1-ahajkova@redhat.com> Date: Tue, 14 Jul 2026 12:23:11 +0100 Message-ID: <87cxwperxc.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: E-y9vEXpgseulo4j8PdmPEjokJhwSf8SNaikz_qvKZI_1784028194 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org 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=C3=A1jkov=C3=A1 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 breakpoin= ts. > > 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 > --- > 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 &=20 > - Fix locspec assignment ordering in adjust_bp_for_source_tracking > - Move the breakpoint_source struct definition from breakpoint.h to break= point.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-sour= ce.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. > =20 > +* 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. > =20 > @@ -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. > =20 > +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. > =20 > 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 wi= th '>'. */ > + > +static void > +breakpoint_source_print (const breakpoint_source *src) > +{ > + if (!breakpoint_source_is_tracked (src)) > + return; > + > + int start_line =3D breakpoint_source_get_start_line (src); > + for (std::size_t j =3D 0; j < src->source_lines.size (); j++) > + { > + int line_num =3D start_line + (int) j; > + char prefix; > + if (j =3D=3D src->bp_line_stored) > +=09prefix =3D '>'; > + else > +=09prefix =3D ' '; > + gdb_printf ("%c %ps %s", prefix, > +=09=09 styled_string (line_number_style.style (), > +=09=09=09=09 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. > +=09=09 src->source_lines[j].c_str ()); > + if (src->source_lines[j].empty () > +=09 || src->source_lines[j].back () !=3D '\n') > +=09gdb_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 =3D=3D nullptr || *args =3D=3D '\0') > + error (_("Breakpoint number required.")); > + > + /* Parse the breakpoint number. */ > + const char *end =3D args; > + int num =3D get_number_trailer (&end, 0); > + > + if (num <=3D 0) > + error (_("Invalid breakpoint number '%s'."), args); > + > + /* Find the breakpoint. */ > + breakpoint *b =3D nullptr; > + for (breakpoint &bp : all_breakpoints ()) > + { > + if (bp.number =3D=3D num) > +=09{ > +=09 b =3D &bp; > +=09 break; > +=09} > + } > + > + if (b =3D=3D 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, > =09=09=09=09enabled, flags, > =09=09=09=09display_canonical); > =20 > + /* 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 locat= ion > + may have moved differently. */ > + if (source_tracking_breakpoints && sals.size () =3D=3D 1 > + && sals[0].explicit_line > + && breakpoint_locspec_suitable_for_tracking (b->locspec.get ())) > + { > + /* Capture source if we have valid symtab and line info. > +=09 This works for both "b file:line" and "b line" formats. > +=09 We capture BREAKPOINT_SRC_CTX_LINES lines to provide > +=09 context around the breakpoint location. */ > + b->bp_source =3D std::make_unique > +=09(breakpoint_source_capture (sals, BREAKPOINT_SRC_CTX_LINES)); > + > + if (!breakpoint_source_is_tracked (b->bp_source.get ())) > +=09warning (_("Source file not available; breakpoint will not be " > +=09=09 "source-tracked.")); Optional: you could change this to: if (!breakpoint_source_is_tracked (b->bp_source.get ())) =09{ =09 warning (_("Source file not available; breakpoint will not be " =09=09 "source-tracked.")); =09 b->bp_source.reset (); =09} 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); > } > =20 > @@ -13183,6 +13487,154 @@ code_breakpoint::location_spec_to_sals (locatio= n_spec *locspec, > return sals; > } > =20 > +/* Match BREAKPOINT_SRC_CTX_LINES lines of the initially stored source i= n a > + BREAKPOINT_SRC_CTX_LINES * BREAKPOINT_SRC_SEARCH_MULTIPLIER lines cur= rent > + source window. > + > + Returns new breakpoint line on success or -1 on failure. */ > + > +static int > +sliding_window_match (breakpoint_source *bp_source, > +=09=09 breakpoint_source *tmp_source) > +{ > + /* The index into BP_SOURCE's lines where the breakpoint was placed. = */ > + size_t bp_stored =3D bp_source->bp_line_stored; > + size_t bp_size =3D 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 &expanded) > +{ > + if (expanded.empty () || expanded[0].symtab =3D=3D nullptr > + || !breakpoint_source_is_tracked (bp_source.get ())) > + return; > + > + struct compunit_symtab &cust =3D expanded[0].symtab->compunit (); > + if (cust.objfile () =3D=3D nullptr) > + return; > + > + bfd *current_bfd =3D cust.objfile ()->obfd.get (); > + if (bp_source->source_bfd.get () =3D=3D current_bfd) > + return; > + > + /* BFD changed =E2=80=94 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 () !=3D 1) > + { > + warning (_("Breakpoint %d now has multiple locations after reload,= " > +=09=09 "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 =3D make_scoped_restore (&source_styling, false); > + if (!g_source_cache.get_source_lines (expanded[0].symtab, > +=09=09=09=09=09expanded[0].line, > +=09=09=09=09=09expanded[0].line, &line)) > + { > + /* Source is unreadable after reload =E2=80=94 drop tracking. */ Non ASCII dash again. > + bp_source.reset (); > + return; > + } > + > + if (line =3D=3D bp_source->source_lines[bp_source->bp_line_stored]) > + { > + /* Line unchanged =E2=80=94 just refresh the capture with the new = BFD. */ Non ASCII dash again. > + bp_source =3D std::make_unique > +=09(breakpoint_source_capture (expanded, BREAKPOINT_SRC_CTX_LINES)); > + return; > + } > + > + breakpoint_source tmp_source > + =3D breakpoint_source_capture (expanded, > +=09=09=09=09 BREAKPOINT_SRC_CTX_LINES > +=09=09=09=09 * BREAKPOINT_SRC_SEARCH_MULTIPLIER); > + int new_bp_line =3D sliding_window_match (bp_source.get (), &tmp_sourc= e); > + if (new_bp_line =3D=3D -1) > + { > + warning (_("Breakpoint %d source code not found " > +=09=09 "after reload, keeping original location."), number); > + bp_source.reset (); > + return; > + } > + > + auto *explicit_loc =3D as_explicit_location_spec (locspec.get ()); > + location_spec_up new_locspec =3D explicit_loc->clone (); > + auto *new_explicit =3D as_explicit_location_spec (new_locspec.get ()); > + new_explicit->line_offset.offset =3D new_bp_line; > + new_explicit->line_offset.sign =3D LINE_OFFSET_NONE; > + new_explicit->set_string (""); /* Invalidate the cached display stri= ng. */ 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 =3D location_spec_to_sals (new_locspec.get (), filter_pspace,= &found); > + if (!found) > + { > + warning (_("Breakpoint %d adjusted to line %d but location could n= ot " > + "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 > +#include > #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. > =20 > +@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. > =20 > @end table > =20 > +@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 . > + > +# 1) Set the breakpoint to a certain line in $srcfile. Replace the $srcf= ile > +# with tmp-$srcfile which is exactly the same except one line is missing= , > +# which changes the line where the breakpoint was initially set and move= s > +# 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 addi= ng 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" \ > +=09"disable" > + > + gdb_test_no_output "set breakpoint source-tracking enabled on" \ > +=09"enable" > +} > + > +# Test that the breakpoint can be adjusted backward. > +set lineno [gdb_get_line_number "var +=3D 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] = =3D=3D -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 ti= me" \ > + "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] = =3D=3D -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 ti= me" \ > + "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 \ > +=09 "1\\s+breakpoint\\s+keep\\s+y\[^\r\n\]+" \ > +=09 "\\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 +=3D 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] = =3D=3D -1} { > + return > +} > + > +# When the original line is removed and cannot be found in the search wi= ndow, > +# 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 with= out 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] = =3D=3D -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 ti= me" \ > + "breakpoint not adjusted when tracking disabled" > + > +# Test that relative line breakpoints (e.g., "b +1") with source trackin= g > +# enabled do not produce a spurious warning. Relative line offsets are = not > +# supported for source tracking, so the breakpoint should be created sil= ently > +# 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 =E2=80=94 not trackable, but sh= ould be Non ASCII dash again. > +# created silently with no warning. > +gdb_test_multiple "break +1" "relative line breakpoint creates no warnin= g" { > + -re -wrap "warning:.*source-track.*" { > +=09fail "$gdb_test_name (unexpected warning)" > + } > + -re -wrap "Breakpoint $decimal at $hex.*" { > +=09pass $gdb_test_name > + } > +} > + Blank line at end of file, please remove. > diff --git a/gdb/testsuite/gdb.base/test_source_tracking.exp b/gdb/testsu= ite/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 . > + > +# 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 clipp= ing > +# 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 b= efore it. > +# Use gdb_test_multiple so the pattern can match the '>' line anywhere i= n 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 $" { > +=09pass $gdb_test_name > + } > + -re "$gdb_prompt $" { > +=09fail $gdb_test_name > + } > +} > + Blank line at end of the file, please remove. Thanks, Andrew