| # Copyright (C) 2025-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 the core file |
| # support in Python. |
| |
| require isnative |
| |
| load_lib gdb-python.exp |
| |
| require allow_python_tests |
| |
| standard_testfile |
| |
| if {[build_executable "build executable" $testfile $srcfile] == -1} { |
| return |
| } |
| |
| set corefile [core_find $binfile] |
| if {$corefile == ""} { |
| unsupported "couldn't create or find corefile" |
| return |
| } |
| |
| # Create a copy of the corefile. |
| set other_corefile [standard_output_file ${testfile}-other.core] |
| remote_exec build "cp $corefile $other_corefile" |
| |
| clean_restart |
| |
| gdb_test_no_output "python inf = gdb.selected_inferior()" \ |
| "capture current inferior" |
| |
| gdb_test "python print(inf.corefile)" "^None" \ |
| "Inferior.corefile is None before loading a core file" |
| |
| gdb_test "core-file $corefile" ".*" \ |
| "load core file" |
| |
| set file_re [string_to_regexp $corefile] |
| gdb_test "python print(inf.corefile)" "^<gdb\\.Corefile inferior=1 filename='$file_re'>" \ |
| "Inferior.corefile is a valid object after loading a core file" |
| |
| gdb_test_no_output "python core1=inf.corefile" "capture gdb.Corefile object" |
| |
| gdb_test "python print(core1.__dict__)" "^\\{\\}" \ |
| "print Corefile.__dict__ when empty" |
| |
| gdb_test_no_output "python core1._my_attribute = \"Hello\"" \ |
| "write new attribute into Corefile object" |
| |
| gdb_test "python print(core1._my_attribute)" "^Hello" \ |
| "immediately read new attribute" |
| |
| gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \ |
| "print Corefile.__dict__ after adding an attribute" |
| |
| gdb_test "python print(core1.filename)" "^$file_re" \ |
| "Corefile.filename attribute works as expected" |
| |
| gdb_test "python print(core1.is_valid())" "^True" \ |
| "Corefile.is_valid() is True while corefile is loaded" |
| |
| gdb_test "core-file" "^No core file now\\." "unload current core file" |
| |
| gdb_test "python print(core1.is_valid())" "^False" \ |
| "Corefile.is_valid() is False after corefile is unloaded" |
| |
| gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \ |
| "print Corefile.__dict__ with attribute when invalid" |
| |
| gdb_test "python print(core1)" "^<gdb\\.Corefile \\(invalid\\)>" \ |
| "print an invalid gdb.Corefile object" |
| |
| gdb_test "python print(core1.filename)" \ |
| [multi_line \ |
| "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \ |
| "Error occurred in Python: Corefile no longer exists\\."] \ |
| "error when reading filename from invalid Corefile" |
| |
| gdb_test "python print(inf.corefile)" "^None" \ |
| "Inferior.corefile is None again after corefile unload" |
| |
| gdb_test "python print(core1._my_attribute)" "^Hello" \ |
| "read new attribute from invalid core file" |
| |
| # Create a second inferior. |
| gdb_test "add-inferior" |
| gdb_test "inferior 2" |
| |
| with_test_prefix "in second inferior" { |
| gdb_test "core-file $corefile" ".*" \ |
| "load core file" |
| |
| gdb_test "python print(inf.corefile)" "^None" \ |
| "first inferior still has no core file" |
| |
| gdb_test_no_output "python core2=gdb.selected_inferior().corefile" \ |
| "capture gdb.Corefile object" |
| |
| # The _my_attribute was added to CORE1, not CORE2. Check it |
| # doesn't somehow appear on CORE2. |
| gdb_test "python print(core2._my_attribute)" \ |
| "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \ |
| "try to read attribute that doesn't exist" |
| |
| gdb_test "python print(core2.filename)" "^$file_re" \ |
| "Corefile.filename attribute works as expected" |
| |
| gdb_test "inferior 1" |
| } |
| |
| # Read the name of the core file from the second program space while |
| # the current program space is the first one. |
| gdb_test "python print(core2.filename)" "^$file_re" \ |
| "Corefile.filename attribute works from different progspace" |
| |
| # Load the other corefile into the first inferior. |
| gdb_test "core $other_corefile" ".*" \ |
| "load other corefile into inferior 1" |
| |
| # Delete the second inferior. We need to switch to the second |
| # inferior and unload its corefile before we can do that. Then, |
| # switch back to the first inferior, delete the second, and try to |
| # read the filename of the core file from the (now deleted) second |
| # inferior. We should get an error about the gdb.Corefile being |
| # invalid. |
| with_test_prefix "remove second inferior" { |
| gdb_test "inferior 2" |
| |
| gdb_test "python print(inf.corefile.filename)" \ |
| "^[string_to_regexp $other_corefile]" \ |
| "read inferior 1 corefile when in inferior 2" |
| |
| gdb_test_no_output "python core1=inf.corefile" \ |
| "capture inferior 1 gdb.Corefile while in inferior 2" |
| |
| # This is a new CORE1 object, check that _my_attribute is gone. |
| gdb_test "python print(core1._my_attribute)" \ |
| "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \ |
| "try to read attribute that doesn't exist" |
| |
| gdb_test "core-file" |
| |
| gdb_test "python print(core2.filename)" \ |
| [multi_line \ |
| "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \ |
| "Error occurred in Python: Corefile no longer exists\\."] \ |
| "error when reading filename from invalid Corefile" |
| |
| gdb_test "inferior 1" |
| |
| gdb_test "remove-inferiors 2" |
| |
| gdb_test "python print(core2.is_valid())" "^False" \ |
| "Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted" |
| |
| gdb_test "python print(core2.filename)" \ |
| [multi_line \ |
| "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \ |
| "Error occurred in Python: Corefile no longer exists\\."] \ |
| "error when reading filename of an invalid Corefile, from deleted program space" |
| |
| gdb_test "python print(core1.is_valid())" "^True" \ |
| "check inferior 1 core file is still valid" |
| } |
| |
| # Test the Corefile.mapped_files() API. The Python script that is |
| # sourced here implements 'info proc mappings' in Python using the |
| # mapped_files API. The output from the built-in command, and the |
| # Python command should be identical. |
| with_test_prefix "test mapped files data" { |
| clean_restart |
| |
| set remote_python_file \ |
| [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] |
| |
| # Load the Python script into GDB. |
| gdb_test "source $remote_python_file" "^Success" \ |
| "source python script" |
| |
| # Load the core file. |
| gdb_test "core-file $corefile" ".*" \ |
| "load core file" |
| |
| # Two files to write the output to. |
| set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt] |
| set out_2 [standard_output_file ${gdb_test_file_name}-out-2.txt] |
| |
| # Run the built-in command, then the new Python command, capture |
| # the output. |
| gdb_test "pipe info proc mappings | tee $out_1" ".*" \ |
| "capture built-in mappings output" |
| gdb_test "pipe info proc py-mappings | tee $out_2" ".*" \ |
| "capture Python based mappings data" |
| |
| # Check the output is identical. |
| gdb_test "shell diff -s $out_1 $out_2" \ |
| "Files \[^\r\n\]+-out-1.txt and \[^\r\n\]+-out-2.txt are identical" \ |
| "diff input and output one" |
| |
| # Check build-ids within the core file mapping data. I'm only |
| # aware of GNU/Linux placing the first page of each mapped ELF |
| # into the generated core file so that the build-id can be found. |
| if {[istarget *-*-linux*]} { |
| set results [list] |
| gdb_test_multiple "show-build-ids" "" { |
| -re "^show-build-ids\r\n" { |
| exp_continue |
| } |
| -re "^Objfile Build-Id\\s+Core File Build-Id\\s+File Name\\s*\r\n" { |
| exp_continue |
| } |
| -re "^(\\S+)\\s+(\\S+)\\s+(\[^\r\n\]+)\r\n" { |
| set objfile_build_id $expect_out(1,string) |
| set core_file_build_id $expect_out(2,string) |
| set file_name $expect_out(3,string) |
| lappend results [list $objfile_build_id \ |
| $core_file_build_id \ |
| $file_name] |
| exp_continue |
| } |
| -re "^$gdb_prompt " { |
| pass $gdb_test_name |
| } |
| } |
| |
| set bad_count 0 |
| foreach entry $results { |
| set objfile_build_id [lindex $entry 0] |
| set core_file_build_id [lindex $entry 1] |
| set file_name [lindex $entry 2] |
| if { $objfile_build_id ne $core_file_build_id } { |
| if { $core_file_build_id ne "None" } { |
| verbose -log "Mismatched build-ids $objfile_build_id vs $core_file_build_id for $file_name" |
| incr bad_count |
| } elseif { [expect_build_id_in_core_file $file_name] } { |
| verbose -log "Failed to find build-id for $file_name" |
| incr bad_count |
| } else { |
| verbose -log "This build-id was likely not in the core file" |
| } |
| } |
| } |
| |
| gdb_assert { $bad_count == 0 } \ |
| "found expected build-ids in core file" |
| } |
| |
| # Check the is_main_executable flag in the mapping data. |
| gdb_test "check-main-executable" "^PASS" |
| |
| # Check that the mapped files "list" is actually an immutable |
| # tuple. |
| gdb_test_no_output "python core = gdb.selected_inferior().corefile" |
| gdb_test_no_output "python mapped_files = core.mapped_files()" |
| gdb_test "python print(type(mapped_files))" \ |
| "^<class 'tuple'>" |
| gdb_test "python mapped_files\[0\] = None" \ |
| "'tuple' object does not support item assignment" |
| gdb_test "python print(mapped_files\[0\] is None)" "^False" |
| |
| # And same for the list of regions for a mapped file. |
| gdb_test_no_output "python regions = mapped_files\[0\].regions" |
| gdb_test "python print(type(regions))" \ |
| "^<class 'tuple'>" |
| gdb_test "python regions\[0\] = None" \ |
| "'tuple' object does not support item assignment" |
| } |