Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tudor-Stefan Magirescu <tudor.magirescu@gmail.com>
To: gdb-patches@sourceware.org
Cc: tudor.magirescu@gmail.com
Subject: [PATCH v2 1/2] [gdb] Fix auto-load script re-execution on subsequent runs
Date: Thu,  3 Sep 2026 11:22:00 +0200	[thread overview]
Message-ID: <20260903092201.663063-2-tudor.magirescu@gmail.com> (raw)
In-Reply-To: <20260903092201.663063-1-tudor.magirescu@gmail.com>

Update maybe_add_script_file and maybe_add_script_text to
unconditionally update the loaded field of an existing hash table entry,
and change callers to gate script execution on whether the script is on
the safe-path, independently of hash table state.

This fixes two related behaviors.  For companion -gdb.py scripts, the
Loaded field shown by "info auto-load" could become stale if the script
was initially blocked by the safe-path but loaded in a subsequent run.
For scripts referenced via .debug_gdb_scripts sections, GDB would not
re-execute the script the second time the associated object file was
loaded, causing per-objfile registered pretty-printers to stop working
after the first run of the inferior.

Add test variants for the .debug_gdb_scripts filename and inline-text
cases to gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event,
verifying that a script blocked on the first run is loaded correctly on
a subsequent run after the safe-path is widened.

Tested on x86_64-linux-gnu and found no regressions.
---
 gdb/auto-load.c                               |  43 +++--
 ...ers-in-newobjfile-event-objfile.so-gdb.py} |   0
 ...s-in-newobjfile-event-dotdebug-filename.py |  43 +++++
 ...-newobjfile-event-lib-dotdebug-filename.cc |  39 +++++
 ...s-in-newobjfile-event-lib-dotdebug-text.cc |  49 ++++++
 ...inters-in-newobjfile-event-lib-objfile.cc} |   0
 ...ed-pretty-printers-in-newobjfile-event.exp | 158 ++++++++++++------
 7 files changed, 259 insertions(+), 73 deletions(-)
 rename gdb/testsuite/gdb.python/{libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py => libpy-autoloaded-pretty-printers-in-newobjfile-event-objfile.so-gdb.py} (100%)
 create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-dotdebug-filename.py
 create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-filename.cc
 create mode 100644 gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-text.cc
 rename gdb/testsuite/gdb.python/{py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc => py-autoloaded-pretty-printers-in-newobjfile-event-lib-objfile.cc} (100%)

diff --git a/gdb/auto-load.c b/gdb/auto-load.c
index ccbd091e1c2..8a6e818fd69 100644
--- a/gdb/auto-load.c
+++ b/gdb/auto-load.c
@@ -676,8 +676,8 @@ get_auto_load_pspace_data_for_loading (struct program_space *pspace)
    LOADED is true if the script has been (is going to) be loaded, false
    otherwise (such as if it has not been found).
    FULL_PATH is NULL if the script wasn't found.
-
-   The result is true if the script was already in the hash table.  */
+   Returns the previous value of the loaded field, or false if the entry
+   did not exist yet.  */
 
 static bool
 maybe_add_script_file (struct auto_load_pspace_info *pspace_info, bool loaded,
@@ -714,18 +714,19 @@ maybe_add_script_file (struct auto_load_pspace_info *pspace_info, bool loaded,
 	}
       else
 	(*slot)->full_path = NULL;
-      (*slot)->loaded = loaded;
       (*slot)->language = language;
     }
 
-  return in_hash_table;
+  bool was_loaded = (*slot)->loaded;
+  (*slot)->loaded = loaded;
+  return was_loaded;
 }
 
 /* Add script contents NAME in LANGUAGE to hash table of PSPACE_INFO.
    LOADED is true if the script has been (is going to) be loaded, false
    otherwise (such as if it has not been found).
-
-   The result is true if the script was already in the hash table.  */
+   Returns the previous value of the loaded field, or false if the entry
+   did not exist yet.  */
 
 static bool
 maybe_add_script_text (struct auto_load_pspace_info *pspace_info,
@@ -753,11 +754,12 @@ maybe_add_script_text (struct auto_load_pspace_info *pspace_info,
       strcpy (p, name);
       (*slot)->name = p;
       (*slot)->full_path = NULL;
-      (*slot)->loaded = loaded;
       (*slot)->language = language;
     }
 
-  return in_hash_table;
+  bool was_loaded = (*slot)->loaded;
+  (*slot)->loaded = loaded;
+  return was_loaded;
 }
 
 /* Clear the table of loaded section scripts.  */
@@ -845,12 +847,7 @@ auto_load_objfile_script_1 (struct objfile *objfile, const char *realname,
 	= get_auto_load_pspace_data_for_loading (objfile->pspace ());
       maybe_add_script_file (pspace_info, is_safe, debugfile, debugfile,
 			     language);
-
-      /* To preserve existing behavior we don't check for whether the
-	 script was already in the table, and always load it.
-	 It's highly unlikely that we'd ever load it twice,
-	 and these scripts are required to be idempotent under multiple
-	 loads anyway.  */
+      /* Execute the script if it is on the safe path.  */
       if (is_safe)
 	{
 	  objfile_script_sourcer_func *sourcer
@@ -1003,13 +1000,12 @@ source_script_file (struct auto_load_pspace_info *pspace_info,
 					    section_name, offset);
     }
 
-  bool in_hash_table
-    = maybe_add_script_file (pspace_info, bool (opened), file,
-			     (opened ? opened->full_path.get (): NULL),
-			     language);
+  maybe_add_script_file (pspace_info, bool (opened), file,
+			 (opened ? opened->full_path.get (): NULL),
+			 language);
 
-  /* If this file is not currently loaded, load it.  */
-  if (opened && !in_hash_table)
+  /* Execute the script if it is on the safe path.  */
+  if (opened)
     sourcer (language, objfile, opened->stream.get (),
 	     opened->full_path.get ());
 }
@@ -1087,11 +1083,10 @@ of file %ps."),
 
   bool is_safe = file_is_auto_load_safe (objfile_name (objfile));
 
-  bool in_hash_table
-    = maybe_add_script_text (pspace_info, is_safe, name, language);
+  maybe_add_script_text (pspace_info, is_safe, name, language);
 
-  /* If this file is not currently loaded, load it.  */
-  if (is_safe && !in_hash_table)
+  /* Execute the script if it is on the safe path.  */
+  if (is_safe)
     executor (language, objfile, name, script_text);
 }
 
diff --git a/gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py b/gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event-objfile.so-gdb.py
similarity index 100%
rename from gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event.so-gdb.py
rename to gdb/testsuite/gdb.python/libpy-autoloaded-pretty-printers-in-newobjfile-event-objfile.so-gdb.py
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-dotdebug-filename.py b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-dotdebug-filename.py
new file mode 100644
index 00000000000..a353358c20d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-dotdebug-filename.py
@@ -0,0 +1,43 @@
+# Copyright (C) 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/>.
+
+# This file is part of the GDB testsuite. It tests that python pretty
+# printers defined in a python script that is autoloaded have been
+# registered when a custom event handler for the new_objfile event
+# is called.
+
+import gdb.printing
+
+
+class MyClassTestLibPrinter(object):
+    "Print a MyClassTestLib"
+
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return "MyClassTestLib object, id: {}".format(self.val["id"])
+
+    def display_hint(self):
+        return "string"
+
+
+def build_pretty_printer():
+    pp = gdb.printing.RegexpCollectionPrettyPrinter("my_library")
+    pp.add_printer("MyClassTestLib", "^MyClassTestLib$", MyClassTestLibPrinter)
+    return pp
+
+
+gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-filename.cc b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-filename.cc
new file mode 100644
index 00000000000..fe31f7083a0
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-filename.cc
@@ -0,0 +1,39 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+#include "py-autoloaded-pretty-printers-in-newobjfile-event-lib.h"
+
+#ifndef SCRIPT_FILE
+#error "SCRIPT_FILE not defined"
+#endif
+
+asm(
+".pushsection \".debug_gdb_scripts\",\"MS\",@progbits,1\n"
+".byte 1\n"
+".asciz \"" SCRIPT_FILE "\"\n"
+".popsection\n"
+);
+
+MyClassTestLib::MyClassTestLib (int theId)
+{
+  id = theId;
+}
+
+int MyClassTestLib::getId ()
+{
+  return id;
+}
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-text.cc b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-text.cc
new file mode 100644
index 00000000000..12d663b362a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-dotdebug-text.cc
@@ -0,0 +1,49 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+#include "py-autoloaded-pretty-printers-in-newobjfile-event-lib.h"
+
+asm(
+".pushsection \".debug_gdb_scripts\",\"MS\",@progbits,1\n"
+".byte 4\n"
+".ascii \"gdb.inlined-script\\n\"\n"
+".ascii \"import gdb.printing\\n\"\n"
+".ascii \"class MyClassTestLibPrinter(object):\\n\"\n"
+".ascii \"    def __init__(self, val):\\n\"\n"
+".ascii \"        self.val = val\\n\"\n"
+".ascii \"    def to_string(self):\\n\"\n"
+".ascii \"        return \\\"MyClassTestLib object, id: {}\\\".format(self.val[\\\"id\\\"])\\n\"\n"
+".ascii \"    def display_hint(self):\\n\"\n"
+".ascii \"        return \\\"string\\\"\\n\"\n"
+".ascii \"def build_pretty_printer():\\n\"\n"
+".ascii \"    pp = gdb.printing.RegexpCollectionPrettyPrinter(\\\"my_library\\\")\\n\"\n"
+".ascii \"    pp.add_printer(\\\"MyClassTestLib\\\", \\\"^MyClassTestLib$\\\", MyClassTestLibPrinter)\\n\"\n"
+".ascii \"    return pp\\n\"\n"
+".ascii \"gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())\\n\"\n"
+".byte 0\n"
+".popsection\n"
+);
+
+MyClassTestLib::MyClassTestLib (int theId)
+{
+  id = theId;
+}
+
+int MyClassTestLib::getId ()
+{
+  return id;
+}
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-objfile.cc
similarity index 100%
rename from gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib.cc
rename to gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event-lib-objfile.cc
diff --git a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
index 9b8a10cf84f..cf0bdb80c51 100644
--- a/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
+++ b/gdb/testsuite/gdb.python/py-autoloaded-pretty-printers-in-newobjfile-event.exp
@@ -15,7 +15,9 @@
 
 # This file is part of the GDB testsuite.  It tests that Python pretty-printers
 # defined in a Python script that is autoloaded are registered when an event
-# handler for the new_objfile event is called.
+# handler for the new_objfile event is called.  It also tests that scripts
+# which were not loaded because the auto-load safe-path did not cover them are
+# loaded on the next run once the safe-path is widened.
 
 load_lib gdb-python.exp
 
@@ -23,70 +25,128 @@ require allow_python_tests
 
 standard_testfile -main.cc
 
-set srcfile_lib "${testfile}-lib.cc"
 set python_event_handler_file "${srcdir}/${subdir}/${testfile}.py"
-set libname "lib${testfile}"
-set python_autoload_file "${srcdir}/${subdir}/${libname}.so-gdb.py"
-set binfile_lib [standard_output_file "${libname}.so"]
-
-# Compile library.
-if { [gdb_compile_shlib ${srcdir}/${subdir}/${srcfile_lib} ${binfile_lib} \
-      {debug c++}] != "" } {
-    return
-}
-
-# Compile main program.
-if { [gdb_compile ${srcdir}/${subdir}/${srcfile} \
-      ${binfile} \
-      executable \
-      [list debug c++ shlib=$binfile_lib]] != "" } {
-    return
-}
-
-clean_restart
+set libscript "lib${testfile}-objfile.so-gdb.py"
+set python_autoload_file "${srcdir}/${subdir}/${libscript}"
 
 # Make the -gdb.py script available to gdb, it is automatically loaded by
 # gdb if it is put in the same directory as the library.
 set remote_python_autoload_file \
     [gdb_remote_download host $python_autoload_file]
 
-gdb_test_no_output \
-    "set auto-load safe-path ${remote_python_autoload_file}" \
-    "set auto-load safe-path"
-
 # Load the Python file that defines a handler for the new_objfile event.
 set remote_python_event_handler_file\
     [gdb_remote_download host $python_event_handler_file]
-gdb_test_no_output "source ${remote_python_event_handler_file}" "load python file"
 
-gdb_load ${binfile}
-gdb_load_shlib $binfile_lib
+proc newobjfile_event_test { variant } {
+    global srcdir subdir testfile srcfile
+    global remote_python_autoload_file remote_python_event_handler_file libscript
+    global decimal
 
-if { ![runto_main] } {
-    return
-}
+    with_test_prefix $variant {
+	if { $variant ne "objfile" && [is_remote host] } {
+	    unsupported "$variant requires non-remote host"
+	    return
+	}
 
-if { [is_remote target ] } {
-    set target_sysroot 0
-    gdb_test_multiple  "show sysroot" "" {
-	-re -wrap "\r\nThe current system root is \"target:.*\"\\." {
-	    set target_sysroot 1
+	set libname "lib${testfile}-${variant}"
+	set binfile_lib [standard_output_file "${libname}.so"]
+	set binfile [standard_output_file "${testfile}-${variant}"]
+
+	set shlib_flags [list debug c++]
+	if { $variant eq "dotdebug-filename" } {
+	    set quoted "\"${srcdir}/${subdir}/${testfile}-${variant}.py\""
+	    lappend shlib_flags additional_flags=-DSCRIPT_FILE=$quoted
 	}
-	-re -wrap "" {
+
+	# Compile library.
+	if { [gdb_compile_shlib \
+	      "${srcdir}/${subdir}/${testfile}-lib-${variant}.cc" \
+	      ${binfile_lib} $shlib_flags] != "" } {
+	    return
+	}
+
+	# Compile main program.
+	if { [gdb_compile ${srcdir}/${subdir}/${srcfile} \
+	      ${binfile} \
+	      executable \
+	      [list debug c++ shlib=$binfile_lib]] != "" } {
+	    return
+	}
+
+	if { $variant eq "objfile" } {
+	    set safe_path_allow $remote_python_autoload_file
+	    set script_name $libscript
+	} elseif { $variant eq "dotdebug-filename" } {
+	    set safe_path_allow "${srcdir}/${subdir}/${testfile}-${variant}.py"
+	    set script_name "${testfile}-${variant}.py"
+	} else {
+	    set safe_path_allow $binfile_lib
+	    set script_name "gdb.inlined-script"
 	}
-    }
 
-    if { $target_sysroot } {
-	unsupported "sysroot start with target: -- auto-load not supported"
-	return
+	clean_restart
+	gdb_test_no_output "source $remote_python_event_handler_file" "load python file"
+	gdb_load ${binfile}
+	gdb_load_shlib $binfile_lib
+
+	if { [is_remote target ] } {
+	    set target_sysroot 0
+	    gdb_test_multiple  "show sysroot" "" {
+		-re -wrap "\r\nThe current system root is \"target:.*\"\\." {
+		    set target_sysroot 1
+		}
+		-re -wrap "" {
+		}
+	    }
+
+	    if { $target_sysroot } {
+		unsupported "sysroot start with target: -- auto-load not supported"
+		return
+	    }
+	}
+
+	gdb_test_no_output "set auto-load safe-path /restricted" \
+	    "set restricted auto-load safe-path"
+
+	if { ![runto_main] } {
+	    return
+	}
+
+	gdb_test_multiple "info auto-load python-scripts" "verify script not loaded" {
+	    -re -wrap "Yes.*${script_name}.*" {
+		fail $gdb_test_name
+	    }
+	    -re -wrap "No.*${script_name}.*" {
+		pass $gdb_test_name
+	    }
+	}
+
+	gdb_test_no_output \
+	    "set auto-load safe-path ${safe_path_allow}" \
+	    "set auto-load safe-path"
+
+	if { ![runto_main] } {
+	    return
+	}
+
+	gdb_test_multiple "info auto-load python-scripts" "verify script loaded" {
+	    -re -wrap "Yes.*${script_name}.*" {
+		pass $gdb_test_name
+	    }
+	    -re -wrap "No.*${script_name}.*" {
+		fail $gdb_test_name
+	    }
+	}
+
+	gdb_test "print all_good" " = true"
+	gdb_test "info pretty-printer" "my_library.*MyClassTestLib.*"
+	gdb_breakpoint [gdb_get_line_number "break to inspect"]
+	gdb_test "continue" "Breakpoint $decimal, main .*"
+	gdb_test "print test" "MyClassTestLib object, id: 1.*"
     }
 }
 
-# Check that the new_objfile handler saw the pretty-printer.
-gdb_test "print all_good" " = true"
-
-# Check that the pretty-printer actually works.
-gdb_test "info pretty-printer" "my_library.*MyClassTestLib.*"
-gdb_breakpoint [gdb_get_line_number "break to inspect"]
-gdb_test "continue" "Breakpoint $decimal, main .*"
-gdb_test "print test" "MyClassTestLib object, id: 1.*"
+newobjfile_event_test "objfile"
+newobjfile_event_test "dotdebug-filename"
+newobjfile_event_test "dotdebug-text"
-- 
2.43.0


  reply	other threads:[~2026-09-03  9:25 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01 14:06 [PATCH] " Tudor-Stefan Magirescu
2026-09-03  9:21 ` [PATCH v2 0/2] Fix auto-load script loading after safe-path is widened Tudor-Stefan Magirescu
2026-09-03  9:22   ` Tudor-Stefan Magirescu [this message]
2026-09-03  9:22   ` [PATCH v2 2/2] [gdb] Retry blocked auto-load scripts when " Tudor-Stefan Magirescu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260903092201.663063-2-tudor.magirescu@gmail.com \
    --to=tudor.magirescu@gmail.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox