Home | History | Annotate | Line # | Download | only in gdb.base
      1 # Copyright 2021-2024 Free Software Foundation, Inc.
      2 
      3 # This program is free software; you can redistribute it and/or modify
      4 # it under the terms of the GNU General Public License as published by
      5 # the Free Software Foundation; either version 3 of the License, or
      6 # (at your option) any later version.
      7 #
      8 # This program is distributed in the hope that it will be useful,
      9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 # GNU General Public License for more details.
     12 #
     13 # You should have received a copy of the GNU General Public License
     14 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15 
     16 # Test that string values are correctly allocated inside GDB when doing
     17 # various operations that yield strings.
     18 #
     19 # The issue that lead to this test was a missing NULL terminator in the
     20 # C-string values.  We verify that we can print the null terminator of these
     21 # strings.
     22 
     23 load_lib "trace-support.exp"
     24 load_lib "gdb-guile.exp"
     25 
     26 standard_testfile
     27 
     28 if {[build_executable "failed to prepare" $testfile $srcfile ]} {
     29     return
     30 }
     31 
     32 set user_conv_funcs {$_gdb_setting $_gdb_setting_str}
     33 set maint_conv_funcs {$_gdb_maint_setting $_gdb_maint_setting_str}
     34 
     35 # Add language (LANG) appropriate quotation marks around string STR.
     36 proc quote_for_lang {lang str} {
     37     if {$lang == "fortran"} {
     38 	return "'$str'"
     39     } else {
     40 	return "\"$str\""
     41     }
     42 }
     43 
     44 # Check that the string contained in the convenienced variable $v is
     45 # EXPECTED_STR.
     46 #
     47 # In particular, check that the null terminator is there and that we can't
     48 # access a character past the end of the string.
     49 
     50 proc check_v_string { expected_str } {
     51     set len [string length $expected_str]
     52 
     53     for { set i 0 } { $i < $len } { incr i } {
     54 	set c [string index $expected_str $i]
     55 	gdb_test "print \$v\[$i\]" "= $::decimal '$c'"
     56     }
     57 
     58     # Check that the string ends with a null terminator.
     59     gdb_test "print \$v\[$i\]" {= 0 '\\000'}
     60 
     61     # Check that we can't access a character after the end of the string.
     62     incr i
     63     gdb_test "print \$v\[$i\]" "no such vector element"
     64 }
     65 
     66 # Test with string values made by $_gdb_setting & co.
     67 
     68 proc_with_prefix test_setting { } {
     69     clean_restart
     70 
     71     # This is an internal GDB implementation detail, but the variable backing
     72     # a string setting starts as nullptr (unless explicitly initialized at
     73     # startup).  When assigning an empty value, the variable then points to an
     74     # empty string.  Test both cases, as it triggers different code paths (in
     75     # addition to a non-empty value).
     76     #
     77     # Use "set trace-user" and "maintenance set test-settings string" as they
     78     # are both not initialized at startup.
     79     with_test_prefix "user setting" {
     80 	with_test_prefix "not set" {
     81 	    foreach_with_prefix conv_func $::user_conv_funcs {
     82 		gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")"
     83 		check_v_string ""
     84 	    }
     85 	}
     86 
     87 	with_test_prefix "set to empty" {
     88 	    gdb_test "set trace-user"
     89 	    foreach_with_prefix conv_func $::user_conv_funcs {
     90 		gdb_test_no_output "set \$v = ${conv_func}(\"trace-user\")"
     91 		check_v_string ""
     92 	    }
     93 	}
     94 
     95 	with_test_prefix "set" {
     96 	    gdb_test "set trace-user poulet"
     97 	    foreach_with_prefix conv_func $::user_conv_funcs {
     98 		gdb_test_no_output {set $v = $_gdb_setting("trace-user")}
     99 		check_v_string "poulet"
    100 	    }
    101 	}
    102     }
    103 
    104     with_test_prefix "maintenance setting" {
    105 	with_test_prefix "not set" {
    106 	    foreach_with_prefix conv_func $::maint_conv_funcs {
    107 		gdb_test_no_output \
    108 		    "set \$v = ${conv_func}(\"test-settings string\")"
    109 		check_v_string ""
    110 	    }
    111 	}
    112 
    113 	with_test_prefix "set to empty" {
    114 	    gdb_test "maintenance set test-settings string"
    115 	    foreach_with_prefix conv_func $::maint_conv_funcs {
    116 		gdb_test_no_output \
    117 		    "set \$v = ${conv_func}(\"test-settings string\")"
    118 		check_v_string ""
    119 	    }
    120 	}
    121 
    122 	with_test_prefix "set" {
    123 	    gdb_test "maintenance set test-settings string perchaude"
    124 	    foreach_with_prefix conv_func $::maint_conv_funcs {
    125 		gdb_test_no_output \
    126 		    "set \$v = ${conv_func}(\"test-settings string\")"
    127 		check_v_string "perchaude"
    128 	    }
    129 	}
    130     }
    131 
    132     # Test with a non-string setting, this tests yet another code path.
    133     with_test_prefix "integer setting" {
    134 	gdb_test_no_output {set $v = $_gdb_setting_str("remotetimeout")}
    135 	check_v_string "2"
    136     }
    137 
    138     # Test string values made by $_gdb_setting & co. in all languages.
    139     with_test_prefix "all langs" {
    140 	# Get list of supported languages.
    141 	set langs [gdb_supported_languages]
    142 
    143 	gdb_test "maintenance set test-settings string foo"
    144 	foreach_with_prefix lang $langs {
    145 	    gdb_test_no_output "set language $lang"
    146 
    147 	    if {$lang == "modula-2"} {
    148 		# The Modula-2 parser doesn't know how to build a
    149 		# suitable string expression.
    150 		gdb_test "print \"foo\"" "strings are not implemented"
    151 		continue
    152 	    }
    153 
    154 	    if {$lang == "rust"} {
    155 		# Rust strings are actually structs, without a running
    156 		# inferior into which the string data can be pushed
    157 		# GDB can't print anything.
    158 		gdb_test "print \"foo\"" \
    159 		    "evaluation of this expression requires the target program to be active"
    160 		gdb_test "print \$_gdb_maint_setting(\"test-settings string\")" \
    161 		    "evaluation of this expression requires the target program to be active"
    162 		continue
    163 	    }
    164 
    165 	    if {$lang == "unknown"} {
    166 		# Skipped because expression parsing is not supported
    167 		# for the "unknown" language.  See gdb/28093 for more
    168 		# details.
    169 		continue
    170 	    }
    171 
    172 	    set print_output ""
    173 	    set ptype_output ""
    174 
    175 	    set foo_str [quote_for_lang $lang foo]
    176 	    gdb_test_multiple "print $foo_str" "" {
    177 		-wrap -re " = (.*)" {
    178 		    set print_output $expect_out(1,string)
    179 		    pass $gdb_test_name
    180 		}
    181 	    }
    182 
    183 	    gdb_test_multiple "ptype $foo_str" "" {
    184 		-wrap -re " = (.*)" {
    185 		    set ptype_output $expect_out(1,string)
    186 		    pass $gdb_test_name
    187 		}
    188 	    }
    189 
    190 	    set cmd_str [quote_for_lang $lang "test-settings string"]
    191 	    set ptype_output_re [string_to_regexp $ptype_output]
    192 	    set print_output_re [string_to_regexp $print_output]
    193 
    194 	    foreach_with_prefix conv_func $::maint_conv_funcs {
    195 		gdb_test "print ${conv_func}($cmd_str)" \
    196 		    " = $print_output_re"
    197 		gdb_test "ptype \$" \
    198 		    " = $ptype_output_re"
    199 	    }
    200 	}
    201     }
    202 }
    203 
    204 # Test with a string value created by gdb.Value in Python.
    205 
    206 proc_with_prefix test_python_value { } {
    207     clean_restart
    208 
    209     if {![allow_python_tests]} {
    210 	untested "skipping test_python_value"
    211 	return
    212     }
    213 
    214     gdb_test_no_output "python gdb.set_convenience_variable(\"v\", \"bar\")" \
    215 	"set convenience var"
    216     check_v_string "bar"
    217 }
    218 
    219 # Test with a string value created by make-value in Guile.
    220 
    221 proc_with_prefix test_guile_value { } {
    222     clean_restart
    223 
    224     if {![allow_guile_tests]} {
    225 	untested "skipping test_guile_value"
    226 	return
    227     }
    228 
    229     # We can't set a convenience var from Guile, but we can append to history.
    230     # Do that, then transfer to a convenience var with a CLI command.
    231     gdb_test_no_output "guile (use-modules (gdb))"
    232     gdb_test_multiple "guile (history-append! (make-value \"foo\"))" "make value" {
    233 	-re -wrap "($::decimal)" {
    234 	    set histnum $expect_out(1,string)
    235 	}
    236     }
    237 
    238     gdb_test_no_output "set \$v = \$$histnum"
    239     check_v_string "foo"
    240 }
    241 
    242 # Test with a string value coming from a string internal var.  The only internal
    243 # vars of this type, at the time of writing, are $trace_func and $trace_file.
    244 # They both require inspecting a trace frame.  So if the target is capable start
    245 # tracing, record one trace frame, and use $trace_func.
    246 
    247 proc_with_prefix test_internal_var { } {
    248     if {![gdb_trace_common_supports_arch]} {
    249 	unsupported "arch does not support trace"
    250 	return
    251     }
    252 
    253     clean_restart $::binfile
    254 
    255     if {![runto_main]} {
    256 	fail "could not run to main"
    257 	return
    258     }
    259 
    260     if {![gdb_target_supports_trace]} {
    261 	unsupported "target does not support trace"
    262 	return
    263     }
    264 
    265     gdb_breakpoint "end"
    266     gdb_test "trace trace_me" "Tracepoint $::decimal at $::hex.*"
    267     gdb_test_no_output "tstart"
    268     gdb_continue_to_breakpoint "breakpoint at end"
    269     gdb_test_no_output "tstop"
    270     gdb_test "tfind" "Found trace frame 0, tracepoint $::decimal.*"
    271     gdb_test_no_output "set \$v = \$trace_func"
    272     gdb_test "tfind none" "No longer looking at any trace frame.*"
    273     check_v_string "trace_me"
    274 }
    275 
    276 test_setting
    277 test_python_value
    278 test_guile_value
    279 test_internal_var
    280