| # Copyright 2021 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 that string values are correctly allocated inside GDB when doing |
| # various operations that yield strings. |
| # |
| # The issue that lead to this test was a missing NULL terminator in the C-string |
| # values. We verify that we can print the null terminator of these strings. |
| |
| load_lib "trace-support.exp" |
| load_lib "gdb-guile.exp" |
| |
| standard_testfile |
| |
| if {[build_executable "failed to prepare" $testfile $srcfile ]} { |
| return |
| } |
| |
| # Check that the string contained in the convenienced variable $v is |
| # EXPECTED_STR. |
| # |
| # In particular, check that the null terminator is there and that we can't |
| # access a character past the end of the string. |
| |
| proc check_v_string { expected_str } { |
| set len [string length $expected_str] |
| |
| for { set i 0 } { $i < $len } { incr i } { |
| set c [string index $expected_str $i] |
| gdb_test "print \$v\[$i\]" "= $::decimal '$c'" |
| } |
| |
| # Check that the string ends with a null terminator. |
| gdb_test "print \$v\[$i\]" {= 0 '\\000'} |
| |
| # Check that we can't access a character after the end of the string. |
| incr i |
| gdb_test "print \$v\[$i\]" "no such vector element" |
| } |
| |
| # Test with string values made by $_gdb_setting & co. |
| |
| proc_with_prefix test_setting { } { |
| clean_restart |
| |
| # This is an internal GDB implementation detail, but the variable backing a |
| # string setting starts as nullptr (unless explicitly initialized at startup). |
| # When assigning an empty value, the variable then points to an empty string. |
| # Test both cases, as it triggers different code paths (in addition to a |
| # non-empty value). |
| # |
| # Use "set trace-user" and "maintenance set test-settings string" as they are |
| # both not initialized at startup. |
| with_test_prefix "user setting" { |
| with_test_prefix "not set" { |
| foreach_with_prefix conv_func {$_gdb_setting $_gdb_setting_str} { |
| gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")" |
| check_v_string "" |
| } |
| } |
| |
| with_test_prefix "set to empty" { |
| gdb_test "set trace-user" |
| foreach_with_prefix conv_func {$_gdb_setting $_gdb_setting_str} { |
| gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")" |
| check_v_string "" |
| } |
| } |
| |
| with_test_prefix "set" { |
| gdb_test "set trace-user poulet" |
| foreach_with_prefix conv_func {$_gdb_setting $_gdb_setting_str} { |
| gdb_test_no_output {set $v = $_gdb_setting("trace-user")} |
| check_v_string "poulet" |
| } |
| } |
| } |
| |
| with_test_prefix "maintenance setting" { |
| with_test_prefix "not set" { |
| foreach_with_prefix conv_func {$_gdb_maint_setting $_gdb_maint_setting_str} { |
| gdb_test_no_output "set \$v = ${conv_func}(\"test-settings string\")" |
| check_v_string "" |
| } |
| } |
| |
| with_test_prefix "set to empty" { |
| gdb_test "maintenance set test-settings string" |
| foreach_with_prefix conv_func {$_gdb_maint_setting $_gdb_maint_setting_str} { |
| gdb_test_no_output "set \$v = ${conv_func}(\"test-settings string\")" |
| check_v_string "" |
| } |
| } |
| |
| with_test_prefix "set" { |
| gdb_test "maintenance set test-settings string perchaude" |
| foreach_with_prefix conv_func {$_gdb_maint_setting $_gdb_maint_setting_str} { |
| gdb_test_no_output "set \$v = ${conv_func}(\"test-settings string\")" |
| check_v_string "perchaude" |
| } |
| } |
| } |
| |
| # Test with a non-string setting, this tests yet another code path. |
| with_test_prefix "integer setting" { |
| gdb_test_no_output {set $v = $_gdb_setting_str("remotetimeout")} |
| check_v_string "2" |
| } |
| } |
| |
| # Test with a string value created by gdb.Value in Python. |
| |
| proc_with_prefix test_python_value { } { |
| clean_restart |
| |
| if {[skip_python_tests]} { |
| untested "skipping test_python_value" |
| return |
| } |
| |
| gdb_test_no_output "python gdb.set_convenience_variable(\"v\", \"bar\")" \ |
| "set convenience var" |
| check_v_string "bar" |
| } |
| |
| # Test with a string value created by make-value in Guile. |
| |
| proc_with_prefix test_guile_value { } { |
| clean_restart |
| |
| if {[skip_guile_tests]} { |
| untested "skipping test_guile_value" |
| return |
| } |
| |
| # We can't set a convenience var from Guile, but we can append to history. |
| # Do that, then transfer to a convenience var with a CLI command. |
| gdb_test_no_output "guile (use-modules (gdb))" |
| gdb_test_multiple "guile (history-append! (make-value \"foo\"))" "make value" { |
| -re -wrap "($::decimal)" { |
| set histnum $expect_out(1,string) |
| } |
| } |
| |
| gdb_test_no_output "set \$v = \$$histnum" |
| check_v_string "foo" |
| } |
| |
| # Test with a string value coming from a string internal var. The only internal |
| # vars of this type, at the time of writing, are $trace_func and $trace_file. |
| # They both require inspecting a trace frame. So if the target is capable start |
| # tracing, record one trace frame, and use $trace_func. |
| |
| proc_with_prefix test_internal_var { } { |
| if {![gdb_trace_common_supports_arch]} { |
| unsupported "arch does not support trace" |
| return |
| } |
| |
| clean_restart $::binfile |
| |
| if {![runto_main]} { |
| fail "could not run to main" |
| return |
| } |
| |
| if {![gdb_target_supports_trace]} { |
| unsupported "target does not support trace" |
| return |
| } |
| |
| gdb_test "break end" "Breakpoint $::decimal at $::hex.*" |
| gdb_test "trace trace_me" "Tracepoint $::decimal at $::hex.*" |
| gdb_test_no_output "tstart" |
| gdb_test "continue" "Breakpoint $::decimal, end.*" |
| gdb_test_no_output "tstop" |
| gdb_test "tfind" "Found trace frame 0, tracepoint $::decimal.*" |
| gdb_test_no_output "set \$v = \$trace_func" |
| gdb_test "tfind none" "No longer looking at any trace frame.*" |
| check_v_string "trace_me" |
| } |
| |
| test_setting |
| test_python_value |
| test_guile_value |
| test_internal_var |