| # 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" | |
| } |