Ensure GDB printf command can print convenience var strings without a target.

Without this patch, GDB printf command calls malloc on the target,
writes the convenience var content to the target,
re-reads the content from the target, and then locally printf the string.

This implies inferior calls, and does not work when there is no running
inferior, or when the inferior is a core dump.

With this patch, printf command can printf string convenience variables
without inferior function calls.
Ada string convenience variables can also be printed.

gdb/ChangeLog
2019-07-08  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* NEWS: Mention that GDB printf and eval commands can now print
	C-style and Ada-style convenience var strings without
	calling the inferior.
	* printcmd.c (printf_c_string): Locally print GDB internal var
	instead of transiting via the inferior.
	(printf_wide_c_string): Likewise.

gdb/testsuite/ChangeLog
2019-07-08  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* gdb.base/printcmds.exp: Test printing C string and
	C wide string convenience vars without transiting via the inferior.
	Also make test names unique.
This commit is contained in:
Philippe Waroquiers
2019-06-10 21:41:51 +02:00
parent ea142fbfc9
commit 1f6f6e21fa
5 changed files with 165 additions and 58 deletions

View File

@ -1,4 +1,13 @@
2019-08-04 Alan Hayward <alan.hayward@arm.com> 2019-07-08 Philippe Waroquiers <philippe.waroquiers@skynet.be>
* NEWS: Mention that GDB printf and eval commands can now print
C-style and Ada-style convenience var strings without
calling the inferior.
* printcmd.c (printf_c_string): Locally print GDB internal var
instead of transiting via the inferior.
(printf_wide_c_string): Likewise.
2019-07-04 Alan Hayward <alan.hayward@arm.com>
* symfile.c (symbol_file_command): Call solib_create_inferior_hook. * symfile.c (symbol_file_command): Call solib_create_inferior_hook.

View File

@ -118,6 +118,13 @@ apropos [-v] REGEXP
of matching commands and to use the highlight style to mark of matching commands and to use the highlight style to mark
the documentation parts matching REGEXP. the documentation parts matching REGEXP.
printf
eval
The GDB printf and eval commands can now print C-style and Ada-style
string convenience variables without calling functions in the program.
This allows to do formatted printing of strings without having
a running inferior, or when debugging a core dump.
show style show style
The "show style" and its subcommands are now styling The "show style" and its subcommands are now styling
a style name in their output using its own style, to help a style name in their output using its own style, to help

View File

@ -23,6 +23,7 @@
#include "gdbtypes.h" #include "gdbtypes.h"
#include "value.h" #include "value.h"
#include "language.h" #include "language.h"
#include "c-lang.h"
#include "expression.h" #include "expression.h"
#include "gdbcore.h" #include "gdbcore.h"
#include "gdbcmd.h" #include "gdbcmd.h"
@ -2222,43 +2223,65 @@ print_variable_and_value (const char *name, struct symbol *var,
/* Subroutine of ui_printf to simplify it. /* Subroutine of ui_printf to simplify it.
Print VALUE to STREAM using FORMAT. Print VALUE to STREAM using FORMAT.
VALUE is a C-style string on the target. */ VALUE is a C-style string either on the target or
in a GDB internal variable. */
static void static void
printf_c_string (struct ui_file *stream, const char *format, printf_c_string (struct ui_file *stream, const char *format,
struct value *value) struct value *value)
{ {
gdb_byte *str; const gdb_byte *str;
CORE_ADDR tem;
int j;
tem = value_as_address (value); if (VALUE_LVAL (value) == lval_internalvar
if (tem == 0) && c_is_string_type_p (value_type (value)))
{ {
DIAGNOSTIC_PUSH size_t len = TYPE_LENGTH (value_type (value));
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
fprintf_filtered (stream, format, "(null)");
DIAGNOSTIC_POP
return;
}
/* This is a %s argument. Find the length of the string. */ /* Copy the internal var value to TEM_STR and append a terminating null
for (j = 0;; j++) character. This protects against corrupted C-style strings that lack
the terminating null char. It also allows Ada-style strings (not
null terminated) to be printed without problems. */
gdb_byte *tem_str = (gdb_byte *) alloca (len + 1);
memcpy (tem_str, value_contents (value), len);
tem_str [len] = 0;
str = tem_str;
}
else
{ {
gdb_byte c; CORE_ADDR tem = value_as_address (value);;
QUIT; if (tem == 0)
read_memory (tem + j, &c, 1); {
if (c == 0) DIAGNOSTIC_PUSH
break; DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
fprintf_filtered (stream, format, "(null)");
DIAGNOSTIC_POP
return;
}
/* This is a %s argument. Find the length of the string. */
size_t len;
for (len = 0;; len++)
{
gdb_byte c;
QUIT;
read_memory (tem + len, &c, 1);
if (c == 0)
break;
}
/* Copy the string contents into a string inside GDB. */
gdb_byte *tem_str = (gdb_byte *) alloca (len + 1);
if (len != 0)
read_memory (tem, tem_str, len);
tem_str[len] = 0;
str = tem_str;
} }
/* Copy the string contents into a string inside GDB. */
str = (gdb_byte *) alloca (j + 1);
if (j != 0)
read_memory (tem, str, j);
str[j] = 0;
DIAGNOSTIC_PUSH DIAGNOSTIC_PUSH
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
fprintf_filtered (stream, format, (char *) str); fprintf_filtered (stream, format, (char *) str);
@ -2267,52 +2290,65 @@ printf_c_string (struct ui_file *stream, const char *format,
/* Subroutine of ui_printf to simplify it. /* Subroutine of ui_printf to simplify it.
Print VALUE to STREAM using FORMAT. Print VALUE to STREAM using FORMAT.
VALUE is a wide C-style string on the target. */ VALUE is a wide C-style string on the target or
in a GDB internal variable. */
static void static void
printf_wide_c_string (struct ui_file *stream, const char *format, printf_wide_c_string (struct ui_file *stream, const char *format,
struct value *value) struct value *value)
{ {
gdb_byte *str; const gdb_byte *str;
CORE_ADDR tem; size_t len;
int j;
struct gdbarch *gdbarch = get_type_arch (value_type (value)); struct gdbarch *gdbarch = get_type_arch (value_type (value));
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
struct type *wctype = lookup_typename (current_language, gdbarch, struct type *wctype = lookup_typename (current_language, gdbarch,
"wchar_t", NULL, 0); "wchar_t", NULL, 0);
int wcwidth = TYPE_LENGTH (wctype); int wcwidth = TYPE_LENGTH (wctype);
gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
tem = value_as_address (value); if (VALUE_LVAL (value) == lval_internalvar
if (tem == 0) && c_is_string_type_p (value_type (value)))
{ {
DIAGNOSTIC_PUSH str = value_contents (value);
DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL len = TYPE_LENGTH (value_type (value));
fprintf_filtered (stream, format, "(null)");
DIAGNOSTIC_POP
return;
} }
else
/* This is a %s argument. Find the length of the string. */
for (j = 0;; j += wcwidth)
{ {
QUIT; CORE_ADDR tem = value_as_address (value);
read_memory (tem + j, buf, wcwidth);
if (extract_unsigned_integer (buf, wcwidth, byte_order) == 0)
break;
}
/* Copy the string contents into a string inside GDB. */ if (tem == 0)
str = (gdb_byte *) alloca (j + wcwidth); {
if (j != 0) DIAGNOSTIC_PUSH
read_memory (tem, str, j); DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
memset (&str[j], 0, wcwidth); fprintf_filtered (stream, format, "(null)");
DIAGNOSTIC_POP
return;
}
/* This is a %s argument. Find the length of the string. */
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
for (len = 0;; len += wcwidth)
{
QUIT;
read_memory (tem + len, buf, wcwidth);
if (extract_unsigned_integer (buf, wcwidth, byte_order) == 0)
break;
}
/* Copy the string contents into a string inside GDB. */
gdb_byte *tem_str = (gdb_byte *) alloca (len + wcwidth);
if (len != 0)
read_memory (tem, tem_str, len);
memset (&tem_str[len], 0, wcwidth);
str = tem_str;
}
auto_obstack output; auto_obstack output;
convert_between_encodings (target_wide_charset (gdbarch), convert_between_encodings (target_wide_charset (gdbarch),
host_charset (), host_charset (),
str, j, wcwidth, str, len, wcwidth,
&output, translit_char); &output, translit_char);
obstack_grow_str0 (&output, ""); obstack_grow_str0 (&output, "");

View File

@ -1,3 +1,9 @@
2019-07-08 Philippe Waroquiers <philippe.waroquiers@skynet.be>
* gdb.base/printcmds.exp: Test printing C string and
C wide string convenience vars without transiting via the inferior.
Also make test names unique.
2019-07-08 Alan Hayward <alan.hayward@arm.com> 2019-07-08 Alan Hayward <alan.hayward@arm.com>
* gdb.base/break-idempotent.exp: Test both PIE and non PIE. * gdb.base/break-idempotent.exp: Test both PIE and non PIE.

View File

@ -438,7 +438,7 @@ proc test_print_repeats_10 {} {
global gdb_prompt decimal global gdb_prompt decimal
for { set x 1 } { $x <= 16 } { incr x } { for { set x 1 } { $x <= 16 } { incr x } {
gdb_test_no_output "set print elements $x" gdb_test_no_output "set print elements $x" "elements $x repeats"
for { set e 1 } { $e <= 16 } {incr e } { for { set e 1 } { $e <= 16 } {incr e } {
set v [expr $e - 1] set v [expr $e - 1]
set command "p &ctable2\[${v}*16\]" set command "p &ctable2\[${v}*16\]"
@ -596,7 +596,7 @@ proc test_print_strings {} {
proc test_print_int_arrays {} { proc test_print_int_arrays {} {
global gdb_prompt global gdb_prompt
gdb_test_no_output "set print elements 24" gdb_test_no_output "set print elements 24" "elements 24 int arrays"
gdb_test_escape_braces "p int1dim" \ gdb_test_escape_braces "p int1dim" \
" = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}" " = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}"
@ -621,7 +621,7 @@ proc test_print_int_arrays {} {
proc test_print_typedef_arrays {} { proc test_print_typedef_arrays {} {
global gdb_prompt global gdb_prompt
gdb_test_no_output "set print elements 24" gdb_test_no_output "set print elements 24" "elements 24 typedef_arrays"
gdb_test_escape_braces "p a1" \ gdb_test_escape_braces "p a1" \
" = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}" " = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}"
@ -666,7 +666,7 @@ proc test_print_char_arrays {} {
global gdb_prompt global gdb_prompt
global hex decimal global hex decimal
gdb_test_no_output "set print elements 24" gdb_test_no_output "set print elements 24" "elements 24 char_arrays"
gdb_test_no_output "set print address on" gdb_test_no_output "set print address on"
gdb_test "p arrays" \ gdb_test "p arrays" \
@ -684,7 +684,7 @@ proc test_print_char_arrays {} {
gdb_test "p parrays->array5" " = \"hij\"" gdb_test "p parrays->array5" " = \"hij\""
gdb_test "p &parrays->array5" " = \\(unsigned char \\(\\*\\)\\\[4\\\]\\) $hex <arrays\\+$decimal>" gdb_test "p &parrays->array5" " = \\(unsigned char \\(\\*\\)\\\[4\\\]\\) $hex <arrays\\+$decimal>"
gdb_test_no_output "set print address off" gdb_test_no_output "set print address off" "address off char arrays"
} }
proc test_print_string_constants {} { proc test_print_string_constants {} {
@ -932,6 +932,42 @@ proc test_repeat_bytes {} {
} }
} }
# Test printf of convenience variables.
# These tests can be done with or without a running inferior.
# PREFIX ensures uniqueness of test names.
# DO_WSTRING 1 tells to test printf of wide strings. Wide strings tests
# must be skipped (DO_WSTRING 0) if the wchar_t type is not yet known by
# GDB, as this type is needed to create wide strings.
proc test_printf_convenience_var {prefix do_wstring} {
with_test_prefix "conv var: $prefix" {
gdb_test_no_output "set var \$cstr = \"abcde\"" "set \$cstr"
gdb_test "printf \"cstr val = %s\\n\", \$cstr" "cstr val = abcde" \
"printf \$cstr"
gdb_test_no_output "set var \$abcde = \"ABCDE\"" "set \$abcde"
gdb_test "eval \"print \$%s\\n\", \$cstr" "= \"ABCDE\"" \
"indirect print abcde"
# Without a target, the below produces no output
# but with a target, it gives a warning.
# So, use gdb_test expecting ".*" instead of gdb_test_no_output.
gdb_test "set language ada" ".*" "set language ada"
gdb_test_no_output "set var \$astr := \"fghij\"" "set \$astr"
gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
"printf \$astr"
gdb_test_no_output "set language auto" "set language auto"
gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
"printf \$astr, auto language"
if {$do_wstring} {
gdb_test_no_output "set var \$wstr = L\"facile\"" \
"set \$wstr"
gdb_test "printf \"wstr val = %ls\\n\", \$wstr" \
"wstr val = facile" "printf \$wstr"
}
}
}
# Start with a fresh gdb. # Start with a fresh gdb.
gdb_exit gdb_exit
@ -948,6 +984,11 @@ gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
gdb_test "print \$cvar = \"abc\"" " = \"abc\"" gdb_test "print \$cvar = \"abc\"" " = \"abc\""
gdb_test "print sizeof (\$cvar)" " = 4" gdb_test "print sizeof (\$cvar)" " = 4"
# Similarly, printf of a string convenience var should work without a target.
# At this point, we cannot create a wide string convenience var, as the
# wchar_t type is not yet known, so skip the wide string tests.
test_printf_convenience_var "no target" 0
# GDB used to complete the explicit location options even when # GDB used to complete the explicit location options even when
# printing expressions. # printing expressions.
gdb_test_no_output "complete p -function" gdb_test_no_output "complete p -function"
@ -977,6 +1018,14 @@ if ![runto_main] then {
return 0 return 0
} }
# With a running target, printf convenience vars should of course work.
test_printf_convenience_var "with target" 1
# It should also work when inferior function calls are forbidden.
gdb_test_no_output "set may-call-functions off"
test_printf_convenience_var "with target, may-call-functions off" 1
gdb_test_no_output "set may-call-functions on"
test_integer_literals_accepted test_integer_literals_accepted
test_integer_literals_rejected test_integer_literals_rejected
test_float_accepted test_float_accepted