# Copyright 1998, 2005, 2007-2012 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 . load_lib "trace-support.exp" set testfile "unavailable" set srcfile ${testfile}.cc set executable $testfile set binfile $objdir/$subdir/$executable if { [gdb_compile "$srcdir/$subdir/$srcfile" $binfile \ executable {debug nowarnings c++}] != "" } { untested unavailable.exp return -1 } set ws "\[\r\n\t \]+" set cr "\[\r\n\]+" if [is_amd64_regs_target] { set fpreg "rbp" set spreg "rsp" set pcreg "rip" } elseif [is_x86_like_target] { set fpreg "ebp" set spreg "esp" set pcreg "eip" } else { set fpreg "fp" set spreg "sp" set pcreg "pc" } # # Utility procs # proc test_register { reg } { global gdb_prompt global hex global cr gdb_test_multiple "print /x $reg" "collected $reg" { -re "\\$\[0-9\]+ = \[x0\]+$cr$gdb_prompt $" { fail "collected $reg (zero)" } -re "\\$\[0-9\]+ = $hex$cr$gdb_prompt $" { pass "collected $reg" } -re "\[Ee\]rror.*$gdb_prompt $" { fail "collected $reg (error)" } } } proc test_register_unavailable { reg } { gdb_test "print /x $reg" \ "" \ "correctly report $reg as unavailable" } proc prepare_for_trace_test {} { global executable clean_restart $executable runto_main gdb_test "break begin" ".*" "" gdb_test "break end" ".*" "" } proc run_trace_experiment { test_func } { global gdb_prompt gdb_test "continue" \ ".*Breakpoint \[0-9\]+, begin .*" \ "advance to begin" gdb_test_no_output "tstart" "start trace experiment" gdb_test "continue" \ "Continuing.*Breakpoint \[0-9\]+, end.*" \ "run trace experiment" gdb_test "tstop" \ "\[\r\n\]+" \ "stop trace experiment" gdb_test "tfind start" \ "#0 $test_func .*" \ "tfind test frame" } # Test that "display VAR" works as expected, assuming VAR is wholly # unavailable. proc test_maybe_regvar_display { var } { global gdb_prompt # Evaluating VAR's location description may throw an internal # "unavailable" exception, if for example, the value of a register # necessary for computing VAR's location is unavailable. Such an # exception is caught, and should not cause automatic disablement # of the current display being printed. (GDB used to disable the # current display whenever any exception was thrown.) set test "display $var" gdb_test_multiple "$test" "$test" { -re "Disabling display ? to avoid infinite recursion.*$gdb_prompt $" { fail "$test" } -re "display ${var}\r\n1: ${var} = \r\n$gdb_prompt $" { pass "$test" } } gdb_test "info display" ".*1:\[ \t\]+y\[ \t\]+${var}" "display ${var} is enabled" gdb_test "undisp" \ "" \ "delete $var display" \ ".*Delete all auto-display expressions.*y or n. $" \ "y" } # # Test procs # proc gdb_collect_args_test {} { global cr global gdb_prompt global pf_prefix set old_pf_prefix $pf_prefix set pf_prefix "$pf_prefix unavailable arguments:" prepare_for_trace_test gdb_test "trace args_test_func" \ "Tracepoint \[0-9\]+ at .*" \ "set tracepoint" # Begin the test. run_trace_experiment args_test_func # Test printing the variables, and also their addresses. We # haven't collected any stack, so there's no way GDB can figure # out the latter. gdb_test "print argc" " = " gdb_test "print &argc" \ "Can't take address of \"argc\" which isn't an lvalue\." gdb_test "print argi" " = " gdb_test "print &argi" \ "Can't take address of \"argi\" which isn't an lvalue\." gdb_test "print argf" " = " gdb_test "print &argf" \ "Can't take address of \"argf\" which isn't an lvalue\." gdb_test "print argd" " = " gdb_test "print &argd" \ "Can't take address of \"argd\" which isn't an lvalue\." # struct arg as one of several args (near end of list) gdb_test "print argstruct" \ " = \{memberc = , memberi = , memberf = , memberd = \}" gdb_test "print argstruct.memberc" " = " gdb_test "print argstruct.memberi" " = " gdb_test "print argstruct.memberf" " = " gdb_test "print argstruct.memberd" " = " gdb_test "print argarray" " = \\(int \\*\\) " gdb_test "print &argarray" \ "Can't take address of \"argarray\" which isn't an lvalue\." gdb_test "print argarray\[0\]" "value is not available" # Test "info args" set r "" set r "${r}argc = ${cr}" set r "${r}argi = ${cr}" set r "${r}argf = ${cr}" set r "${r}argd = ${cr}" set r "${r}argstruct = {memberc = , memberi = , memberf = , memberd = }${cr}" set r "${r}argarray = ${cr}" gdb_test "info args" "$r" "info args" test_maybe_regvar_display "argc" gdb_test "tfind none" \ "#0 end .*" \ "cease trace debugging" set pf_prefix $old_pf_prefix } proc gdb_collect_locals_test { func msg } { global cr global gdb_prompt global pf_prefix set old_pf_prefix $pf_prefix set pf_prefix "$pf_prefix unavailable locals: $msg:" prepare_for_trace_test set testline [gdb_get_line_number "set $func tracepoint here"] gdb_test "trace $testline" \ "Tracepoint \[0-9\]+ at .*" \ "set tracepoint" # Begin the test. run_trace_experiment $func gdb_test "print locc" " = " gdb_test "print loci" " = " gdb_test "print locf" " = " gdb_test "print locd" " = " gdb_test "print locst.memberc" " = " gdb_test "print locst.memberi" " = " gdb_test "print locst.memberf" " = " gdb_test "print locst.memberd" " = " gdb_test "print locar\[0\]" " = " gdb_test "print locar\[1\]" " = " gdb_test "print locar\[2\]" " = " gdb_test "print locar\[3\]" " = " # Test "info locals" set r "" set r "${r}locf = ${cr}" set r "${r}locd = ${cr}" set r "${r}locst = {memberc = , memberi = , memberf = , memberd = }${cr}" set r "${r}locar = {, , , }${cr}" set r "${r}i = ${cr}" if { $func == "local_test_func" } { set r "${r}locdefst = {}${cr}" } set r "${r}locc = ${cr}" set r "${r}loci = ${cr}" gdb_test "info locals" "$r" "info locals" test_maybe_regvar_display "loci" gdb_test "tfind none" \ "#0 end .*" \ "cease trace debugging" set pf_prefix $old_pf_prefix } proc gdb_unavailable_registers_test { } { global gdb_prompt global spreg global pcreg global pf_prefix set old_pf_prefix $pf_prefix set pf_prefix "$pf_prefix unavailable registers:" prepare_for_trace_test # We'll simply re-use the globals_test_function for this test gdb_test "trace globals_test_func" \ "Tracepoint \[0-9\]+ at .*" \ "set tracepoint" # Collect nothing. # Begin the test. run_trace_experiment globals_test_func # On some archs, the $sp/$pc are a real raw registers. On others, # like x86, they're user registers. Test both variants. test_register_unavailable "\$$spreg" test_register_unavailable "\$sp" # Test reading uncollected pseudo-registers. The set of which # depends on target. if [is_amd64_regs_target] { # Check the raw register first. test_register_unavailable "\$rax" test_register_unavailable "\$eax" test_register_unavailable "\$ax" } elseif [is_x86_like_target] { # Check the raw register first. test_register_unavailable "\$eax" test_register_unavailable "\$ax" } # GDBserver always provides the PC value of regular tracepoint # hits, since it's the same as the tracepoint's address. test_register "\$$pcreg" test_register "\$pc" gdb_test "info registers" \ "\\*value not available\\*.*\\*value not available\\*" \ "info registers, multiple registers not available" gdb_test "info registers \$$spreg" \ "\\*value not available\\*" \ "info registers \$$spreg reports not available" gdb_test "tfind none" "#0 end .*" "cease trace debugging" set pf_prefix $old_pf_prefix } proc gdb_collect_globals_test { } { global ws global cr global gdb_prompt global hex global pf_prefix set old_pf_prefix $pf_prefix set pf_prefix "$pf_prefix collect globals:" prepare_for_trace_test set testline [gdb_get_line_number "set globals_test_func tracepoint here"] gdb_test "trace $testline" \ "Tracepoint \[0-9\]+ at .*" \ "set tracepoint" # We collect the initial sizeof(pointer) bytes of derived_partial # in an attempt of collecting the vptr. Not portable, but should # work everywhere we need to care. gdb_trace_setactions "define actions" \ "" \ "collect struct_b.struct_a.array\[2\]" "^$" \ "collect struct_b.struct_a.array\[100\]" "^$" \ \ "collect a" "^$" \ "collect c" "^$" \ \ "collect tarray\[0\].a" "^$" \ "collect tarray\[1\].a" "^$" \ "collect tarray\[3\].a" "^$" \ "collect tarray\[3\].b" "^$" \ "collect tarray\[4\].b" "^$" \ "collect tarray\[5\].b" "^$" \ \ "collect g_string_p" "^$" \ "collect g_string_partial\[1\]" "^$" \ "collect g_string_partial\[2\]" "^$" \ \ "collect g_structref_p" "^$" \ \ "collect *((char *)&derived_partial)@sizeof\(void *\)" "^$" \ "collect derived_whole" "^$" \ \ "collect virtual_partial.z" "^$" # Begin the test. run_trace_experiment globals_test_func gdb_test "print globalc" " = " gdb_test "print globali" " = " gdb_test "print globalf" " = " gdb_test "print globald" " = " gdb_test "print globalstruct.memberc" " = " gdb_test "print globalstruct.memberi" " = " gdb_test "print globalstruct.memberf" " = " gdb_test "print globalstruct.memberd" " = " gdb_test "print globalstruct" \ " = {memberc = , memberi = , memberf = , memberd = }" gdb_test "print globalp == &globalstruct" \ "value is not available" \ "can't compare using non collected global pointer" gdb_test "print globalarr\[1\]" " = " gdb_test "print globalarr\[2\]" " = " gdb_test "print globalarr\[3\]" " = " gdb_test "print struct_b" \ " = {d = , ef = , struct_a = {a = , b = , array = {, , -1431655766, , -1431655766, }, ptr = , bitfield = }, s = , static static_struct_a = {a = , b = , array = { }, ptr = , bitfield = }, string = }" gdb_test "print /x struct_b" \ " = {d = , ef = , struct_a = {a = , b = , array = {, , 0xaaaaaaaa, , 0xaaaaaaaa, }, ptr = , bitfield = }, s = , static static_struct_a = {a = , b = , array = { }, ptr = , bitfield = }, string = }" gdb_test "print /x struct_b.struct_a" \ " = {a = , b = , array = {, , 0xaaaaaaaa, , 0xaaaaaaaa, }, ptr = , bitfield = }" gdb_test "print /x struct_b.struct_a.array" \ " = {, , 0xaaaaaaaa, , 0xaaaaaaaa, }" gdb_test "print /x struct_b.struct_a.array\[0\]" " = " gdb_test "print /x struct_b.struct_a.array\[2\]" " = 0xaaaaaaaa" # Check the target doesn't overcollect. GDB used to merge memory # ranges to collect if they were close enough (collecting the hole # as well), but does not do that anymore. It's plausible that a # target may do this on its end, but as of this writing, no known # target does it. gdb_test "print {a, b, c}" \ " = \\{1, , 3\\}" \ "No overcollect of almost but not quite adjacent memory ranges" # Check isn't confused with 0 in array element repetitions gdb_test_no_output "set print repeat 1" gdb_test "print /x tarray" \ " = \{\{a = 0x0, b = \} , \{a = , b = \}, \{a = 0x0, b = 0x0\}, \{a = , b = 0x0\} , \{a = , b = \} \}" \ " is not the same as 0 in array element repetitions" gdb_test_no_output "set print repeat 10" # Check that value repeat handles unavailable-ness. gdb_test "print *tarray@3" " = \\{\\{a = 0, b = \\}, \\{a = 0, b = \\}, \\{a = , b = \\}\\}" # Static fields gdb_test "print struct_b.static_struct_a" \ " = {a = , b = , array = { }, ptr = , bitfield = }" # Bitfields gdb_test "print struct_b.struct_a.bitfield" " = " # References gdb_test "print g_int" " = " gdb_test "print g_ref" \ "\\(int &\\) @$hex: " \ "global reference shows address but not value" gdb_test "print *&g_ref" \ "\\$\[0-9\]+ = $cr" \ "referenced integer was not collected (taking address of reference)" gdb_test "print *g_structref_p" \ " = {d = , ref = }" # Strings # Const string is always available, even when not collected. gdb_test "print g_const_string" \ " = \"hello world\"$cr" \ "non collected const string is still printable" gdb_test "print g_string_p" \ " = $hex \"hello world\"" \ "printing constant string through collected pointer" gdb_test "print g_string_unavail" \ " = \{ \}" \ "printing non collected string" # Incomplete strings print as an array. gdb_test "print g_string_partial" \ "\\$\[0-9\]+ = \{, 101 'e', 108 'l', , , , , , , , , \}" \ "printing partially collected string" # It is important for this test that the last examined value is # , to exercise the case of the $__ convenience # variable being set to without error. set msg "examining partially collected object" gdb_test_multiple "x /10x &struct_b" "$msg" { -re "$hex :${ws}${ws}${ws}${ws}$cr$hex :${ws}${ws}${ws}0xaaaaaaaa${ws}$cr$hex :${ws}${ws}$cr$gdb_prompt $" { pass "$msg" } -re "value is not available" { fail "$msg" } } gdb_test "p \$__" " = " "last examined value was " # This tests that building the array does not require accessing # g_int's contents. gdb_test "print { 1, g_int, 3 }" \ " = \\{1, , 3\\}" \ "build array from unavailable value" # Note, depends on previous test. gdb_test "print \$\[1\]" \ " = " \ "subscript a non-memory rvalue array, accessing an unvailable element" # Access a field of a non-lazy value, making sure the # unavailable-ness is propagated. History values are easy # non-lazy values, so use those. The first test just sets up for # the second. gdb_test "print g_smallstruct" " = \\{member = \\}" gdb_test "print \$.member" " = " # Cast to baseclass, checking the unavailable-ness is propagated. gdb_test "print (small_struct) g_smallstruct_b" " = \\{member = \\}" # Same cast, but starting from a non-lazy, value. gdb_test "print g_smallstruct_b" " = \\{ = \\{member = \\}, \\}" gdb_test "print (small_struct) \$" " = \\{member = \\}" gdb_test_no_output "set print object on" set old_pf_prefix_2 $pf_prefix set pf_prefix "$pf_prefix print object on:" # With print object on, printing a pointer may need to fetch the # pointed-to object, to check its run-time type. Make sure that # fails gracefully and transparently when the pointer itself is # unavailable. gdb_test "print virtualp" " = \\(Virtual \\*\\) " # no vtable pointer available gdb_test "print derived_unavail" \ " = { = , _vptr.Derived = , z = }" # vtable pointer available, but nothing else gdb_test "print derived_partial" \ " = \\(Derived\\) { = { = , _vptr.Middle = , y = }, _vptr.Derived = $hex, z = }" # whole object available gdb_test "print derived_whole" \ " = \\(Derived\\) { = { = {x = 2}, _vptr.Middle = $hex, y = 3}, _vptr.Derived = $hex, z = 4}" set pf_prefix $old_pf_prefix_2 gdb_test_no_output "set print object off" set pf_prefix "$pf_prefix print object off:" gdb_test "print virtualp" " = \\(Virtual \\*\\) " # no vtable pointer available gdb_test "print derived_unavail" \ " = { = , _vptr.Derived = , z = }" # vtable pointer available, but nothing else gdb_test "print derived_partial" \ " = { = { = , _vptr.Middle = , y = }, _vptr.Derived = $hex, z = }" # whole object available gdb_test "print derived_whole" \ " = { = { = {x = 2}, _vptr.Middle = $hex, y = 3}, _vptr.Derived = $hex, z = 4}" set pf_prefix $old_pf_prefix_2 # An instance of a virtual class where we collected everything but # the vptr. gdb_test "print virtual_partial" \ " = {_vptr.Virtual = , z = 0}" gdb_test "tfind none" \ "#0 end .*" \ "cease trace debugging" set pf_prefix $old_pf_prefix } proc gdb_trace_collection_test {} { gdb_collect_globals_test gdb_unavailable_registers_test gdb_collect_args_test gdb_collect_locals_test local_test_func "auto locals" gdb_collect_locals_test reglocal_test_func "register locals" gdb_collect_locals_test statlocal_test_func "static locals" } clean_restart $executable runto_main # We generously give ourselves one "pass" if we successfully # detect that this test cannot be run on this target! if { ![gdb_target_supports_trace] } then { pass "Current target does not support trace" return 1; } # Body of test encased in a proc so we can return prematurely. gdb_trace_collection_test # Finished! gdb_test "tfind none" ".*" ""