@@ -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.
Â
@@ -118,6 +118,13 @@ apropos [-v] REGEXP
   of matching commands and to use the highlight style to mark
   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
   The "show style" and its subcommands are now styling
   a style name in their output using its own style, to help
@@ -23,6 +23,7 @@
 #include "gdbtypes.h"
 #include "value.h"
 #include "language.h"
+#include "c-lang.h"
 #include "expression.h"
 #include "gdbcore.h"
 #include "gdbcmd.h"
@@ -2222,42 +2223,64 @@ print_variable_and_value (const char *name, struct symbol *var,
Â
 /* Subroutine of ui_printf to simplify it.
    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
 printf_c_string (struct ui_file *stream, const char *format,
  struct value *value)
 {
-Â Â gdb_byte *str;
-Â Â CORE_ADDR tem;
-Â Â int j;
+Â Â const gdb_byte *str;
Â
-Â Â tem = value_as_address (value);
-Â Â if (tem == 0)
+Â Â if (VALUE_LVAL (value) == lval_internalvar
+Â Â Â Â Â Â && c_is_string_type_p (value_type (value)))
     {
-Â Â Â Â Â Â DIAGNOSTIC_PUSH
-Â Â Â Â Â Â DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
-Â Â Â Â Â Â fprintf_filtered (stream, format, "(null)");
-Â Â Â Â Â Â DIAGNOSTIC_POP
-Â Â Â Â Â Â return;
-Â Â Â Â }
+Â Â Â Â Â Â size_t len = TYPE_LENGTH (value_type (value));
Â
-  /* This is a %s argument.  Find the length of the string.  */
-Â Â for (j = 0;; j++)
-Â Â Â Â {
-Â Â Â Â Â Â gdb_byte c;
+Â Â Â Â Â Â /* Copy the internal var value to TEM_STR and append a terminating null
+  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);
Â
-Â Â Â Â Â Â QUIT;
-Â Â Â Â Â Â read_memory (tem + j, &c, 1);
-Â Â Â Â Â Â if (c == 0)
- break;
+Â Â Â Â Â Â memcpy (tem_str, value_contents (value), len);
+Â Â Â Â Â Â tem_str [len] = 0;
+Â Â Â Â Â Â str = tem_str;
     }
+Â Â else
+Â Â Â Â {
+Â Â Â Â Â Â CORE_ADDR tem = value_as_address (value);;
+
+Â Â Â Â Â Â if (tem == 0)
+ {
+ Â Â DIAGNOSTIC_PUSH
+ Â Â 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;
Â
-  /* 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;
+ Â Â 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;
+Â Â Â Â }
Â
   DIAGNOSTIC_PUSH
   DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
@@ -2267,52 +2290,65 @@ printf_c_string (struct ui_file *stream, const char *format,
Â
 /* Subroutine of ui_printf to simplify it.
    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
 printf_wide_c_string (struct ui_file *stream, const char *format,
       struct value *value)
 {
-Â Â gdb_byte *str;
-Â Â CORE_ADDR tem;
-Â Â int j;
+Â Â const gdb_byte *str;
+Â Â size_t len;
   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,
  "wchar_t", NULL, 0);
   int wcwidth = TYPE_LENGTH (wctype);
-Â Â gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
Â
-Â Â tem = value_as_address (value);
-Â Â if (tem == 0)
+Â Â if (VALUE_LVAL (value) == lval_internalvar
+Â Â Â Â Â Â && c_is_string_type_p (value_type (value)))
     {
-Â Â Â Â Â Â DIAGNOSTIC_PUSH
-Â Â Â Â Â Â DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
-Â Â Â Â Â Â fprintf_filtered (stream, format, "(null)");
-Â Â Â Â Â Â DIAGNOSTIC_POP
-Â Â Â Â Â Â return;
+Â Â Â Â Â Â str = value_contents (value);
+Â Â Â Â Â Â len = TYPE_LENGTH (value_type (value));
     }
-
-  /* This is a %s argument.  Find the length of the string.  */
-Â Â for (j = 0;; j += wcwidth)
+Â Â else
     {
-Â Â Â Â Â Â QUIT;
-Â Â Â Â Â Â read_memory (tem + j, buf, wcwidth);
-Â Â Â Â Â Â if (extract_unsigned_integer (buf, wcwidth, byte_order) == 0)
- break;
-Â Â Â Â }
+Â Â Â Â Â Â CORE_ADDR tem = value_as_address (value);
Â
-  /* Copy the string contents into a string inside GDB.  */
-Â Â str = (gdb_byte *) alloca (j + wcwidth);
-Â Â if (j != 0)
-Â Â Â Â read_memory (tem, str, j);
-Â Â memset (&str[j], 0, wcwidth);
+Â Â Â Â Â Â if (tem == 0)
+ {
+ Â Â DIAGNOSTIC_PUSH
+ Â Â DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+ Â Â 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;
Â
   convert_between_encodings (target_wide_charset (gdbarch),
      host_charset (),
- Â Â Â Â Â str, j, wcwidth,
+ Â Â Â Â Â str, len, wcwidth,
      &output, translit_char);
   obstack_grow_str0 (&output, "");
Â
@@ -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>
Â
 * gdb.base/break-idempotent.exp: Test both PIE and non PIE.
@@ -438,7 +438,7 @@ proc test_print_repeats_10 {} {
     global gdb_prompt decimal
Â
     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 } {
     set v [expr $e - 1]
     set command "p &ctable2\[${v}*16\]"
@@ -596,7 +596,7 @@ proc test_print_strings {} {
 proc test_print_int_arrays {} {
     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" \
 " = {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 {} {
     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" \
 " = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}"
@@ -666,7 +666,7 @@ proc test_print_char_arrays {} {
     global gdb_prompt
     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 "p arrays" \
@@ -684,7 +684,7 @@ proc test_print_char_arrays {} {
     gdb_test "p parrays->array5" " = \"hij\""
     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 {} {
@@ -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.
Â
 gdb_exit
@@ -948,6 +984,11 @@ gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
 gdb_test "print \$cvar = \"abc\"" " = \"abc\""
 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
 # printing expressions.
 gdb_test_no_output "complete p -function"
@@ -977,6 +1018,14 @@ if ![runto_main] then {
     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_rejected
 test_float_accepted