mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-23 13:21:43 +08:00
The fact that GDB defaults to assuming that functions return int, when it has no debug info for the function has been a recurring source of user confusion. Recently this came up on the errno pretty printer discussions. Shortly after, it came up again on IRC, with someone wondering why does getenv() in GDB return a negative int: (gdb) p getenv("PATH") $1 = -6185 This question (with s/getenv/random-other-C-runtime-function) is a FAQ on IRC. The reason for the above is: (gdb) p getenv $2 = {<text variable, no debug info>} 0x7ffff7751d80 <getenv> (gdb) ptype getenv type = int () ... which means that GDB truncated the 64-bit pointer that is actually returned from getent to 32-bit, and then sign-extended it: (gdb) p /x -6185 $6 = 0xffffe7d7 The workaround is to cast the function to the right type, like: (gdb) p ((char *(*) (const char *)) getenv) ("PATH") $3 = 0x7fffffffe7d7 "/usr/local/bin:/"... IMO, we should do better than this. I see the "assume-int" issue the same way I see printing bogus values for optimized-out variables instead of "<optimized out>" -- I'd much rather that the debugger tells me "I don't know" and tells me how to fix it than showing me bogus misleading results, making me go around tilting at windmills. If GDB prints a signed integer when you're expecting a pointer or aggregate, you at least have some sense that something is off, but consider the case of the function actually returning a 64-bit integer. For example, compile this without debug info: unsigned long long function () { return 0x7fffffffffffffff; } Currently, with pristine GDB, you get: (gdb) p function () $1 = -1 # incorrect (gdb) p /x function () $2 = 0xffffffff # incorrect maybe after spending a few hours debugging you suspect something is wrong with that -1, and do: (gdb) ptype function type = int () and maybe, just maybe, you realize that the function actually returns unsigned long long. And you try to fix it with: (gdb) p /x (unsigned long long) function () $3 = 0xffffffffffffffff # incorrect ... which still produces the wrong result, because GDB simply applied int to unsigned long long conversion. Meaning, it sign-extended the integer that it extracted from the return of the function, to 64-bits. and then maybe, after asking around on IRC, you realize you have to cast the function to a pointer of the right type, and call that. It won't be easy, but after a few missteps, you'll get to it: ..... (gdb) p /x ((unsigned long long(*) ()) function) () $666 = 0x7fffffffffffffff # finally! :-) So to improve on the user experience, this patch does the following (interrelated) things: - makes no-debug-info functions no longer default to "int" as return type. Instead, they're left with NULL/"<unknown return type>" return type. (gdb) ptype getenv type = <unknown return type> () - makes calling a function with unknown return type an error. (gdb) p getenv ("PATH") 'getenv' has unknown return type; cast the call to its declared return type - and then to make it easier to call the function, makes it possible to _only_ cast the return of the function to the right type, instead of having to cast the function to a function pointer: (gdb) p (char *) getenv ("PATH") # now Just Works $3 = 0x7fffffffe7d7 "/usr/local/bin:/"... (gdb) p ((char *(*) (const char *)) getenv) ("PATH") # continues working $4 = 0x7fffffffe7d7 "/usr/local/bin:/"... I.e., it makes GDB default the function's return type to the type of the cast, and the function's parameters to the type of the arguments passed down. After this patch, here's what you'll get for the "unsigned long long" example above: (gdb) p function () 'function' has unknown return type; cast the call to its declared return type (gdb) p /x (unsigned long long) function () $4 = 0x7fffffffffffffff # correct! Note that while with "print" GDB shows the name of the function that has the problem: (gdb) p getenv ("PATH") 'getenv' has unknown return type; cast the call to its declared return type which can by handy in more complicated expressions, "ptype" does not: (gdb) ptype getenv ("PATH") function has unknown return type; cast the call to its declared return type This will be fixed in the next patch. gdb/ChangeLog: 2017-09-04 Pedro Alves <palves@redhat.com> * ada-lang.c (ada_evaluate_subexp) <TYPE_CODE_FUNC>: Don't handle TYPE_GNU_IFUNC specially here. Throw error if return type is unknown. * ada-typeprint.c (print_func_type): Handle functions with unknown return type. * c-typeprint.c (c_type_print_base): Handle functions and methods with unknown return type. * compile/compile-c-symbols.c (convert_symbol_bmsym) <mst_text_gnu_ifunc>: Use nodebug_text_gnu_ifunc_symbol. * compile/compile-c-types.c: Include "objfiles.h". (convert_func): For functions with unknown return type, warn and default to int. * compile/compile-object-run.c (compile_object_run): Adjust call to call_function_by_hand_dummy. * elfread.c (elf_gnu_ifunc_resolve_addr): Adjust call to call_function_by_hand. * eval.c (evaluate_subexp_standard): Adjust calls to call_function_by_hand. Handle functions and methods with unknown return type. Pass expect_type to call_function_by_hand. * f-typeprint.c (f_type_print_base): Handle functions with unknown return type. * gcore.c (call_target_sbrk): Adjust call to call_function_by_hand. * gdbtypes.c (objfile_type): Leave nodebug text symbol with NULL return type instead of int. Make nodebug_text_gnu_ifunc_symbol be an integer address type instead of nodebug. * guile/scm-value.c (gdbscm_value_call): Adjust call to call_function_by_hand. * infcall.c (error_call_unknown_return_type): New function. (call_function_by_hand): New "default_return_type" parameter. Pass it down. (call_function_by_hand_dummy): New "default_return_type" parameter. Use it instead of defaulting to int. If there's no default and the return type is unknown, throw an error. If there's a default return type, and the called function has no debug info, then assume the function is prototyped. * infcall.h (call_function_by_hand, call_function_by_hand_dummy): New "default_return_type" parameter. (error_call_unknown_return_type): New declaration. * linux-fork.c (call_lseek): Cast return type of lseek. (inferior_call_waitpid, checkpoint_command): Adjust calls to call_function_by_hand. * linux-tdep.c (linux_infcall_mmap, linux_infcall_munmap): Adjust calls to call_function_by_hand. * m2-typeprint.c (m2_procedure): Handle functions with unknown return type. * objc-lang.c (lookup_objc_class, lookup_child_selector) (value_nsstring, print_object_command): Adjust calls to call_function_by_hand. * p-typeprint.c (pascal_type_print_varspec_prefix): Handle functions with unknown return type. (pascal_type_print_func_varspec_suffix): New function. (pascal_type_print_varspec_suffix) <TYPE_CODE_FUNC, TYPE_CODE_METHOD>: Use it. * python/py-value.c (valpy_call): Adjust call to call_function_by_hand. * rust-lang.c (rust_evaluate_funcall): Adjust call to call_function_by_hand. * valarith.c (value_x_binop, value_x_unop): Adjust calls to call_function_by_hand. * valops.c (value_allocate_space_in_inferior): Adjust call to call_function_by_hand. * typeprint.c (type_print_unknown_return_type): New function. * typeprint.h (type_print_unknown_return_type): New declaration. gdb/testsuite/ChangeLog: 2017-09-04 Pedro Alves <palves@redhat.com> * gdb.base/break-main-file-remove-fail.exp (test_remove_bp): Cast return type of munmap in infcall. * gdb.base/break-probes.exp: Cast return type of foo in infcall. * gdb.base/checkpoint.exp: Simplify using for loop. Cast return type of ftell in infcall. * gdb.base/dprintf-detach.exp (dprintf_detach_test): Cast return type of getpid in infcall. * gdb.base/infcall-exec.exp: Cast return type of execlp in infcall. * gdb.base/info-os.exp: Cast return type of getpid in infcall. Bail on failure to extract the pid. * gdb.base/nodebug.c: #include <stdint.h>. (multf, multf_noproto, mult, mult_noproto, add8, add8_noproto): New functions. * gdb.base/nodebug.exp (test_call_promotion): New procedure. Change expected output of print/whatis/ptype with functions with no debug info. Test all supported languages. Call test_call_promotion. * gdb.compile/compile.exp: Adjust expected output to expect warning. * gdb.threads/siginfo-threads.exp: Likewise.
341 lines
11 KiB
Plaintext
341 lines
11 KiB
Plaintext
# Copyright 2005-2017 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/>. */
|
|
|
|
if { [is_remote target] || ![isnative] } then {
|
|
continue
|
|
}
|
|
|
|
# Until "set follow-fork-mode" and "catch fork" are implemented on
|
|
# other targets...
|
|
#
|
|
if {![istarget "*-*-linux*"]} then {
|
|
continue
|
|
}
|
|
|
|
# Must name the source file explicitly, otherwise when driven by
|
|
# checkpoints-ns.exp, we'd try compiling checkpoints-ns.c, which
|
|
# doesn't exist.
|
|
standard_testfile checkpoint.c
|
|
|
|
set pi_txt [gdb_remote_download host ${srcdir}/${subdir}/pi.txt]
|
|
if {[is_remote host]} {
|
|
set copy1_txt copy1.txt
|
|
} else {
|
|
set copy1_txt [standard_output_file copy1.txt]
|
|
}
|
|
|
|
if {[prepare_for_testing "failed to prepare" $testfile $srcfile \
|
|
[list debug "additional_flags=-DPI_TXT=\"$pi_txt\" -DCOPY1_TXT=\"$copy1_txt\""]]} {
|
|
return -1
|
|
}
|
|
|
|
global gdb_prompt
|
|
|
|
#
|
|
# This tests gdb checkpoint and restart.
|
|
#
|
|
|
|
runto_main
|
|
set break1_loc [gdb_get_line_number "breakpoint 1"]
|
|
set break2_loc [gdb_get_line_number "breakpoint 2"]
|
|
set break3_loc [gdb_get_line_number "breakpoint 3"]
|
|
set break4_loc [gdb_get_line_number "breakpoint 4"]
|
|
|
|
gdb_breakpoint $break1_loc
|
|
gdb_test "continue" "breakpoint 1.*" "break1 start"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 two"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 three"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 four"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 five"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 six"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 seven"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 eight"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 nine"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
gdb_test "continue 10" "breakpoint 1.*" "break1 ten"
|
|
|
|
gdb_test "checkpoint" ".*" ""
|
|
|
|
gdb_test "info checkpoints" \
|
|
" 1 .* 2 .* 3 .* 4 .* 5 .* 6 .* 7 .* 8 .* 9 .* 10 .*" \
|
|
"info checkpoints one"
|
|
|
|
delete_breakpoints
|
|
gdb_breakpoint $break2_loc
|
|
gdb_test "continue" "breakpoint 2.*" "break2 one"
|
|
|
|
gdb_test "restart 1" "Switching to .*breakpoint 1.*" "restart 1 one"
|
|
gdb_test "print i" " = 78" "verify i 1 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 1 one"
|
|
gdb_test "print lines" " = 1.*" "verify lines 1 one"
|
|
|
|
gdb_test "restart 2" "Switching to .*breakpoint 1.*" "restart 2 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 2 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 2 one"
|
|
gdb_test "print lines" " = 11.*" "verify lines 2 one"
|
|
|
|
gdb_test "restart 3" "Switching to .*breakpoint 1.*" "restart 3 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 3 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 3 one"
|
|
gdb_test "print lines" " = 21.*" "verify lines 3 one"
|
|
|
|
gdb_test "restart 4" "Switching to .*breakpoint 1.*" "restart 4 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 4 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 4 one"
|
|
gdb_test "print lines" " = 31.*" "verify lines 4 one"
|
|
|
|
gdb_test "restart 5" "Switching to .*breakpoint 1.*" "restart 5 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 5 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 5 one"
|
|
gdb_test "print lines" " = 41.*" "verify lines 5 one"
|
|
|
|
gdb_test "restart 6" "Switching to .*breakpoint 1.*" "restart 6 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 6 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 6 one"
|
|
gdb_test "print lines" " = 51.*" "verify lines 6 one"
|
|
|
|
gdb_test "restart 7" "Switching to .*breakpoint 1.*" "restart 7 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 7 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 7 one"
|
|
gdb_test "print lines" " = 61.*" "verify lines 7 one"
|
|
|
|
gdb_test "restart 8" "Switching to .*breakpoint 1.*" "restart 8 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 8 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 8 one"
|
|
gdb_test "print lines" " = 71.*" "verify lines 8 one"
|
|
|
|
gdb_test "restart 9" "Switching to .*breakpoint 1.*" "restart 9 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 9 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 9 one"
|
|
gdb_test "print lines" " = 81.*" "verify lines 9 one"
|
|
|
|
gdb_test "restart 10" "Switching to .*breakpoint 1.*" "restart 10 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 10 one"
|
|
gdb_test "print i + 1 == lines * 79" " = 1" "verify i 10 one"
|
|
gdb_test "print lines" " = 91.*" "verify lines 10 one"
|
|
|
|
#
|
|
# Now let the files be closed by the original process,
|
|
# and diff them.
|
|
|
|
gdb_test "restart 0" "Switching to .*breakpoint 2.*" "restart 0 one"
|
|
gdb_breakpoint $break3_loc
|
|
gdb_test "continue" "breakpoint 3.*" "break3 one"
|
|
|
|
gdb_test "shell diff -s $pi_txt $copy1_txt" \
|
|
"Files .*pi.txt and .*copy1.txt are identical.*" \
|
|
"diff input and output one"
|
|
|
|
#
|
|
# And now run from various checkpoints, allowing
|
|
# various amounts of input and output.
|
|
#
|
|
|
|
gdb_breakpoint $break1_loc
|
|
|
|
gdb_test "restart 1" "Switching to .*c == EOF.*" "restart 1 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 100" "breakpoint 1.*" "breakpoint 1 1 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 1 two"
|
|
gdb_test "print lines" " = 102.*" "verify lines 1 two"
|
|
|
|
gdb_test "restart 2" "Switching to .*c == EOF.*" "restart 2 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 100" "breakpoint 1.*" "breakpoint 1 2 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 2 two"
|
|
gdb_test "print lines" " = 112.*" "verify lines 2 two"
|
|
|
|
gdb_test "restart 3" "Switching to .*c == EOF.*" "restart 3 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 500" "breakpoint 1.*" "breakpoint 1 3 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 3 two"
|
|
gdb_test "print lines" " = 522.*" "verify lines 3 two"
|
|
|
|
gdb_test "restart 4" "Switching to .*c == EOF.*" "restart 4 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 500" "breakpoint 1.*" "breakpoint 1 4 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 4 two"
|
|
gdb_test "print lines" " = 532.*" "verify lines 4 two"
|
|
|
|
gdb_test "restart 5" "Switching to .*c == EOF.*" "restart 5 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 1000" "breakpoint 1.*" "breakpoint 1 5 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 5 two"
|
|
gdb_test "print lines" " = 1042.*" "verify lines 5 two"
|
|
|
|
gdb_test "restart 6" "Switching to .*c == EOF.*" "restart 6 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 1000" "breakpoint 1.*" "breakpoint 1 6 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 6 two"
|
|
gdb_test "print lines" " = 1052.*" "verify lines 5 two"
|
|
|
|
gdb_test "restart 7" "Switching to .*c == EOF.*" "restart 7 two"
|
|
gdb_test "continue" ".*" ""
|
|
gdb_test "continue 1100" "breakpoint 1.*" "breakpoint 1 7 one"
|
|
gdb_test "step" "if .c == EOF.*" "step in 7 two"
|
|
gdb_test "print lines" " = 1162.*" "verify lines 7 two"
|
|
|
|
gdb_test "shell diff -s $pi_txt $copy1_txt" \
|
|
"Files .*pi.txt and .*copy1.txt are identical.*" \
|
|
"diff input and output two"
|
|
|
|
#
|
|
# OK, now allow the original program to delete the output file,
|
|
# and verify that the checkpoints can still write to it.
|
|
#
|
|
|
|
gdb_test "restart 0" "Switching to .*breakpoint 3.*" "restart 0 one"
|
|
gdb_breakpoint $break4_loc
|
|
gdb_test "continue" "breakpoint 4.*" "break4 one"
|
|
|
|
gdb_test "shell diff $pi_txt $copy1_txt" \
|
|
"diff: .*copy1.txt: No such file or directory" \
|
|
"delete copy1"
|
|
|
|
delete_breakpoints
|
|
gdb_breakpoint $break2_loc
|
|
|
|
for {set num 1} {$num <= 10} {incr num} {
|
|
gdb_test "restart $num" "if .c == EOF.*" "restart $num three"
|
|
gdb_test "continue" "breakpoint 2.*" "break2 $num one"
|
|
gdb_test "print (long) ftell (out) > 100000" " = 1.*" "outfile still open $num"
|
|
}
|
|
|
|
#
|
|
# Now confirm that if one fork exits, we automatically switch to another one.
|
|
#
|
|
|
|
delete_breakpoints
|
|
gdb_test "continue" \
|
|
"Deleting copy.*$inferior_exited_re normally.*Switching to.*" \
|
|
"exit, dropped into next fork one"
|
|
|
|
gdb_test "continue" \
|
|
"Deleting copy.*$inferior_exited_re normally.*Switching to.*" \
|
|
"exit, dropped into next fork two"
|
|
|
|
gdb_test "continue" \
|
|
"Deleting copy.*$inferior_exited_re normally.*Switching to.*" \
|
|
"exit, dropped into next fork three"
|
|
|
|
gdb_test "continue" \
|
|
"Deleting copy.*$inferior_exited_re normally.*Switching to.*" \
|
|
"exit, dropped into next fork four"
|
|
|
|
gdb_test "continue" \
|
|
"Deleting copy.*$inferior_exited_re normally.*Switching to.*" \
|
|
"exit, dropped into next fork five"
|
|
|
|
#
|
|
# There should be still at least five forks left
|
|
#
|
|
|
|
gdb_test "info checkpoints" " 1 .* 2 .* 3 .* 4 .* 5 .*" \
|
|
"info checkpoints two"
|
|
|
|
#
|
|
# Kill should now terminate all of them.
|
|
#
|
|
|
|
gdb_test "kill" "" "kill all one" \
|
|
"Kill the program being debugged.*y or n. $" "y"
|
|
|
|
#
|
|
# and confirm that all are gone
|
|
#
|
|
|
|
gdb_test "restart 0" "Not found.*" "no more checkpoint 0"
|
|
gdb_test "restart 1" "Not found.*" "no more checkpoint 1"
|
|
gdb_test "restart 2" "Not found.*" "no more checkpoint 2"
|
|
gdb_test "restart 3" "Not found.*" "no more checkpoint 3"
|
|
gdb_test "restart 4" "Not found.*" "no more checkpoint 4"
|
|
gdb_test "restart 5" "Not found.*" "no more checkpoint 5"
|
|
gdb_test "restart 6" "Not found.*" "no more checkpoint 6"
|
|
gdb_test "restart 7" "Not found.*" "no more checkpoint 7"
|
|
gdb_test "restart 8" "Not found.*" "no more checkpoint 8"
|
|
gdb_test "restart 9" "Not found.*" "no more checkpoint 9"
|
|
gdb_test "restart 10" "Not found.*" "no more checkpoint 10"
|
|
|
|
#
|
|
# Now let's try setting a large number of checkpoints (>600)
|
|
#
|
|
|
|
gdb_exit
|
|
gdb_start
|
|
gdb_reinitialize_dir $srcdir/$subdir
|
|
gdb_load ${binfile}
|
|
|
|
runto_main
|
|
gdb_breakpoint $break1_loc
|
|
|
|
gdb_test "commands\nsilent\nif (lines % 2)\ncheckpoint\nend\n continue\nend" \
|
|
"" \
|
|
"set checkpoint breakpoint"
|
|
|
|
set prev_timeout $timeout
|
|
set timeout [expr $timeout + 120]
|
|
verbose "Timeout now $timeout sec."
|
|
|
|
gdb_breakpoint $break2_loc
|
|
gdb_test "continue" "breakpoint 2.*" "break2 with many checkpoints"
|
|
|
|
set count 0
|
|
set msg "info checkpoints with at least 600 checkpoints"
|
|
gdb_test_multiple "info checkpoints" $msg {
|
|
-re " $decimal process \[^\r\]*\r\n" {
|
|
incr count
|
|
exp_continue
|
|
}
|
|
-re "$gdb_prompt $" {
|
|
if { $count >= 600 } {
|
|
pass $msg
|
|
} else {
|
|
fail $msg
|
|
}
|
|
}
|
|
}
|
|
|
|
#
|
|
# OK, kill 'em all...
|
|
#
|
|
|
|
gdb_test "kill" "" "kill all one with many checkpoints" \
|
|
"Kill the program being debugged.*y or n. $" "y"
|
|
|
|
# Restore old timeout
|
|
set timeout $prev_timeout
|
|
verbose "Timeout now $timeout sec."
|
|
|
|
#
|
|
# Finished: cleanup
|
|
#
|