# Copyright 2021-2023 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 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 } set user_conv_funcs {$_gdb_setting $_gdb_setting_str} set maint_conv_funcs {$_gdb_maint_setting $_gdb_maint_setting_str} # Add language (LANG) appropriate quotation marks around string STR. proc quote_for_lang {lang str} { if {$lang == "fortran"} { return "'$str'" } else { return "\"$str\"" } } # 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 $::user_conv_funcs { 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 $::user_conv_funcs { 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 $::user_conv_funcs { 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 $::maint_conv_funcs { 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 $::maint_conv_funcs { 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 $::maint_conv_funcs { 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 string values made by $_gdb_setting & co. in all languages. with_test_prefix "all langs" { # Get list of supported languages. set langs [gdb_supported_languages] gdb_test "maintenance set test-settings string foo" foreach_with_prefix lang $langs { gdb_test_no_output "set language $lang" if {$lang == "modula-2"} { # The Modula-2 parser doesn't know how to build a # suitable string expression. gdb_test "print \"foo\"" "strings are not implemented" continue } if {$lang == "rust"} { # Rust strings are actually structs, without a running # inferior into which the string data can be pushed # GDB can't print anything. gdb_test "print \"foo\"" \ "evaluation of this expression requires the target program to be active" gdb_test "print \$_gdb_maint_setting(\"test-settings string\")" \ "evaluation of this expression requires the target program to be active" continue } if {$lang == "unknown"} { # Skipped because expression parsing is not supported # for the "unknown" language. See gdb/28093 for more # details. continue } set print_output "" set ptype_output "" set foo_str [quote_for_lang $lang foo] gdb_test_multiple "print $foo_str" "" { -wrap -re " = (.*)" { set print_output $expect_out(1,string) pass $gdb_test_name } } gdb_test_multiple "ptype $foo_str" "" { -wrap -re " = (.*)" { set ptype_output $expect_out(1,string) pass $gdb_test_name } } set cmd_str [quote_for_lang $lang "test-settings string"] set ptype_output_re [string_to_regexp $ptype_output] set print_output_re [string_to_regexp $print_output] foreach_with_prefix conv_func $::maint_conv_funcs { gdb_test "print ${conv_func}($cmd_str)" \ " = $print_output_re" gdb_test "ptype \$" \ " = $ptype_output_re" } } } } # Test with a string value created by gdb.Value in Python. proc_with_prefix test_python_value { } { clean_restart if {![allow_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 {![allow_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_breakpoint "end" gdb_test "trace trace_me" "Tracepoint $::decimal at $::hex.*" gdb_test_no_output "tstart" gdb_continue_to_breakpoint "breakpoint at 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