Patchwork [RFA] Ensure GDB printf command can print convenience var strings without a target.

login
register
mail settings
Submitter Philippe Waroquiers
Date June 10, 2019, 9:16 p.m.
Message ID <20190610211622.15237-1-philippe.waroquiers@skynet.be>
Download mbox | patch
Permalink /patch/33074/
State New
Headers show

Comments

Philippe Waroquiers - June 10, 2019, 9:16 p.m.
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 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-06-10  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-06-10  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

	* gdb.base/printcmds.exp: Test printing C strings and
	C wide strings convenience var without transiting via the inferior.
---
 gdb/NEWS                             |   7 ++
 gdb/printcmd.c                       | 143 +++++++++++++++++----------
 gdb/testsuite/gdb.base/printcmds.exp |  39 ++++++++
 3 files changed, 136 insertions(+), 53 deletions(-)
Eli Zaretskii - June 11, 2019, 2:29 a.m.
> From: Philippe Waroquiers <philippe.waroquiers@skynet.be>
> Cc: Philippe Waroquiers <philippe.waroquiers@skynet.be>
> Date: Mon, 10 Jun 2019 23:16:22 +0200
> 
> 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 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-06-10  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-06-10  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
> 
> 	* gdb.base/printcmds.exp: Test printing C strings and
> 	C wide strings convenience var without transiting via the inferior.
> ---
>  gdb/NEWS                             |   7 ++
>  gdb/printcmd.c                       | 143 +++++++++++++++++----------
>  gdb/testsuite/gdb.base/printcmds.exp |  39 ++++++++
>  3 files changed, 136 insertions(+), 53 deletions(-)

The NEWS part is OK, thanks.

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index 9e1462b6bf..9d6a2de661 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -98,6 +98,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
+  convenience variables without calling functions in the program.
+  This allows to do formatted printing of strings without having
+  an 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
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 9e84594fe6..d7b8b9a1c1 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -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"
@@ -2200,91 +2201,127 @@  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 on the target or a C-style string
+   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;
-    }
+      gdb_byte *tem_str;
+      size_t len  = TYPE_LENGTH (value_type (value));
 
-  /* This is a %s argument.  Find the length of the string.  */
-  for (j = 0;; j++)
+      /* Copy the internal var value to tem_str and append a terminating null
+	 character.  This protects against corrupted C-style strings that lacks
+	 the terminating null char.  It also allows Ada style strings (not not
+	 null terminated) to be printed without problems.  */
+      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;
+      int len;
+      CORE_ADDR tem;
+      gdb_byte *tem_str;
 
-      QUIT;
-      read_memory (tem + j, &c, 1);
-      if (c == 0)
-	break;
-    }
+      tem = value_as_address (value);
+      if (tem == 0)
+	{
+	  DIAGNOSTIC_PUSH
+	    DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+	    fprintf_filtered (stream, format, "(null)");
+	  DIAGNOSTIC_POP
+	    return;
+	}
 
-  /* 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;
+      /* This is a %s argument.  Find the length of the string.  */
+      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.  */
+      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
-  fprintf_filtered (stream, format, (char *) str);
+    DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL
+    fprintf_filtered (stream, format, (char *) str);
   DIAGNOSTIC_POP
-}
+    }
 
 /* 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 a wide C-style string
+   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;
+  const gdb_byte *str;
   int j;
   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);
+      j = 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;
-    }
+      gdb_byte *tem_str;
+      CORE_ADDR tem;
+      gdb_byte *buf = (gdb_byte *) alloca (wcwidth);
 
-  /* 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);
+      enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+      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.  */
+      for (j = 0;; j += wcwidth)
+	{
+	  QUIT;
+	  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.  */
+      tem_str = (gdb_byte *) alloca (j + wcwidth);
+      if (j != 0)
+	read_memory (tem, tem_str, j);
+      memset (&tem_str[j], 0, wcwidth);
+      str = tem_str;
+    }
 
   auto_obstack output;
 
diff --git a/gdb/testsuite/gdb.base/printcmds.exp b/gdb/testsuite/gdb.base/printcmds.exp
index f2d6ee229d..3b6562426e 100644
--- a/gdb/testsuite/gdb.base/printcmds.exp
+++ b/gdb/testsuite/gdb.base/printcmds.exp
@@ -932,6 +932,32 @@  proc test_repeat_bytes {} {
     }
 }
 
+proc test_printf_convenience_var {prefix do_wstring} {
+
+    with_test_prefix $prefix {
+	gdb_test_no_output "set var \$cstr = \"abcde\"" "set \$cstr, conv var"
+	gdb_test "printf \"cstr val = %s\\n\", \$cstr" "cstr val = abcde" \
+	    "printf \$cstr, conv var"
+	gdb_test_no_output "set var \$abcde = \"ABCDE\"" "set \$abcde, conv var"
+	gdb_test "eval \"print \$%s\\n\", \$cstr" "= \"ABCDE\"" \
+	    "indirect print abcde"
+	gdb_test "set language ada" ".*" "set language ada, conv var"
+	gdb_test_no_output "set var \$astr := \"fghij\"" "set \$astr, conv var"
+	gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
+	    "printf \$astr, conv var"
+	gdb_test "set language auto" ".*" "set language auto, conv var"
+	gdb_test "printf \"astr val = %s\\n\", \$astr" "astr val = fghij" \
+	    "printf \$astr, conv var, auto language"
+	if ($do_wstring) {
+	    gdb_test_no_output "set var \$wstr = L\"facile\"" \
+		"set \$wstr, conv var"
+	    gdb_test "printf \"wstr val = %ls\\n\", \$wstr" \
+		"wstr val = facile" "printf \$wstr, conv var"
+	}
+    }
+}
+
+
 # Start with a fresh gdb.
 
 gdb_exit
@@ -948,6 +974,11 @@  gdb_test "ptype \"abc\"" " = char \\\[4\\\]"
 gdb_test "print \$cvar = \"abc\"" " = \"abc\""
 gdb_test "print sizeof (\$cvar)" " = 4"
 
+# Similarly, printf of convenience var should work without a target.
+# At this point, we cannot create wide strings convenience var, as the
+# type wchar_t 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 +1008,14 @@  if ![runto_main] then {
     return 0
 }
 
+# With a target, printf convenience var should of course work.
+test_printf_convenience_var "with target" 1
+
+# But 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