public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Add gdb.Value.format_string ()
@ 2019-03-16 11:18 Marco Barisione
  2019-03-17 20:50 ` Kevin Buettner
  0 siblings, 1 reply; 16+ messages in thread
From: Marco Barisione @ 2019-03-16 11:18 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 915 bytes --]

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

The attached patch introduces a new format_string () method to gdb.Value
which allows specifying more formatting options, thus giving access to
more features provided by the internal C function common_val_print ().


This patch is longer than I usually like for my patches, but it's a
single feature and I didn't think it was particularly useful to split it
in a patch adding the C function, one for the test, one for the docs.
Let me know if you disagree and would prefer me to split it in some way.

I've never contributed to GDB before, so I filled up and signed all the
legal documents, but I haven't received the final document signed by the
FSF yet.

Thanks!




[-- Attachment #2: 0001-Add-gdb.Value.format_string.patch --]
[-- Type: application/octet-stream, Size: 52998 bytes --]

From 10caf698e95781044bcce71f80b6bc6fdcfde96f Mon Sep 17 00:00:00 2001
From: Marco Barisione <mbarisione@undo.io>
Date: Sat, 16 Mar 2019 09:37:12 +0000
Subject: [PATCH] Add gdb.Value.format_string ()

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

This patch introduces a new format_string () method to gdb.Value which
allows specifying more formatting options, thus giving access to more
features provided by the internal C function common_val_print ().

gdb/ChangeLog:

2019-03-16  Marco Barisione  <mbarisione@undo.io>

	Add gdb.Value.format_string ().
	* python/py-value.c (copy_py_bool_obj):
	(valpy_format_string): Add gdb.Value.format_string ().
	* NEWS: Document the addition of gdb.Value.format_string ().

gdb/doc/ChangeLog:

2019-03-16  Marco Barisione  <mbarisione@undo.io>

	* python.texi: Document gdb.Value.format_string ()

gdb/testsuite/ChangeLog:

2019-03-16  Marco Barisione  <mbarisione@undo.io>

	Test gdb.Value.format_string ().
	* gdb.python/py-format-string.exp: New test.
	* gdb.python/py-format-string.c: New file.
	* gdb.python/py-format-string.py: New file.
---
 gdb/NEWS                                      |   6 +
 gdb/doc/python.texi                           |  79 ++
 gdb/python/py-value.c                         | 161 +++
 gdb/testsuite/gdb.python/py-format-string.c   | 118 +++
 gdb/testsuite/gdb.python/py-format-string.exp | 958 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-format-string.py  |  49 +
 6 files changed, 1371 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.c
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.exp
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.py

diff --git a/gdb/NEWS b/gdb/NEWS
index c45b313406..e4d9ae0fea 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -231,6 +231,12 @@ before Windows XP.
   ** The gdb.Value type has a new constructor, which is used to construct a
      gdb.Value from a Python buffer object and a gdb.Type.
 
+  ** The gdb.Value type has a new method 'format_string' which returns a
+     string representing the value.  The formatting is controlled by the
+     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
+     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
+     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
+
 * Configure changes
 
 --enable-ubsan
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index b02a154ce1..71d6d344fb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -864,6 +864,85 @@ Like @code{Value.cast}, but works as if the C@t{++} @code{reinterpret_cast}
 operator were used.  Consult a C@t{++} reference for details.
 @end defun
 
+@defun Value.format_string (...)
+Convert a @code{gdb.Value} to a string, similarly to what the @code{print}
+command does.  Invoked with no arguments, this is equivalent to calling
+the @code{str} function on the @code{gdb.Value}.  The representation of
+the same value may change across different versions of @value{GDBN}, so
+you shouldn't, for instance, parse the strings returned by this method.
+
+All the arguments are keyword only.  If an argument is not specified, the
+current global default setting is used.
+
+@table @code
+@item raw
+@code{True} if pretty-printers (@pxref{Pretty Printing}) should not be
+used to format the value.  @code{False} if enabled pretty-printers
+matching the type represented by the @code{gdb.Value} should be used to
+format it.
+
+@item pretty_arrays
+@code{True} if arrays should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print array} in
+@ref{Print Settings}).
+
+@item pretty_structs
+@code{True} if structs should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print pretty} in
+@ref{Print Settings}).
+
+@item array_indexes
+@code{True} if array indexes should be included in the string
+representation of arrays, @code{False} if they shouldn't (see @code{set
+print array-indexes} in @ref{Print Settings}).
+
+@item symbols
+@code{True} if the string representation of a pointer should include the
+corresponding symbol name (if one exists), @code{False} if it shouldn't
+(see @code{set print symbol} in @ref{Print Settings}).
+
+@item unions
+@code{True} if unions which are contained in other structures or unions
+should be expanded, @code{False} if they shouldn't (see @code{set print
+union} in @ref{Print Settings}).
+
+@item deref_refs
+@code{True} if C++ references should be resolved to the value they refer
+to, @code{False} (the default) if they shouldn't.  Note that, unlike for
+the @code{print} command, references are not automatically expanded when
+using the @code{format_string ()} method or the @code{str ()} function.
+There is no global @code{print} setting to change the default behaviour.
+
+@item actual_objects
+@code{True} if the representation of a pointer to an object should
+identify the @emph{actual} (derived) type of the object rather than the
+@emph{declared} type, using the virtual function table.  @code{False} if
+the @emph{declared} type should be used.  (See @code{set print object} in
+@ref{Print Settings}).
+
+@item static_fields
+@code{True} if static members should be included in the string
+representation of a C++ object, @code{False} if they shouldn't (see
+@code{set print static-members} in @ref{Print Settings}).
+
+@item max_elements
+Number of array elements to print, or @code{0} to print an unlimited
+number of elements (see @code{set print elements} in @ref{Print
+Settings}).
+
+@item repeat_threshold
+Set the threshold for suppressing display of repeated array elements, or
+@code{0} to represent all elements, even if repeated.  (See @code{set
+print repeats} in @ref{Print Settings}).
+
+@item format
+A string containing a single character representing the format to use for
+the returned string.  For instance, @code{'x'} is equivalent to using the
+@value{GDBN} command @code{print} with the @code{/x} option and formats
+the value as a hexadecimal number.
+@end table
+@end defun
+
 @defun Value.string (@r{[}encoding@r{[}, errors@r{[}, length@r{]]]})
 If this @code{gdb.Value} represents a string, then this method
 converts the contents to a Python string.  Otherwise, this method will
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index dd6a536b6a..46a0f563e4 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -588,6 +588,162 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
 			   encoding, errors);
 }
 
+/* Given a Python object, copy its truth value to a C int (the value
+   pointed by dest).
+   If src_obj is NULL, then *dest is not modified.
+
+   Return 1 in case of success, -1 otherwise.  */
+
+static int
+copy_py_bool_obj (int *dest, PyObject *src_obj)
+{
+  if (src_obj)
+    {
+      int cmp = PyObject_IsTrue (src_obj);
+      if (cmp < 0)
+	return -1;
+      *dest = cmp;
+    }
+
+  return 1;
+}
+
+/* Implementation of gdb.Value.format_string (...) -> string.
+   Return Unicode string with value contents formatted using the
+   keyword-only arguments.  */
+
+static PyObject *
+valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = {
+      /* Basic C/C++ options.  */
+      "raw",			/* See the /r option to print.  */
+      "pretty_arrays",		/* See set print array on|off.  */
+      "pretty_structs",		/* See set print pretty on|off.  */
+      "array_indexes",		/* See set print array-indexes on|off.  */
+      "symbols",		/* See set print symbol on|off.  */
+      "unions",			/* See set print union on|off.  */
+      /* C++ options.  */
+      "deref_refs",		/* No corresponding setting.  */
+      "actual_objects",		/* See set print object on|off.  */
+      "static_members",		/* See set print static-members on|off.  */
+      /* C non-bool options.  */
+      "max_elements", 		/* See set print elements N.  */
+      "repeat_threshold",	/* See set print repeats.  */
+      "format",			/* The format passed to the print command.  */
+      NULL };
+
+  /* This function has too many arguments to be useful as positionals, so
+     the user should specify them all as keyword arguments.
+     Python 3.3 and later have a way to specify it (both in C and Python
+     itself), but we could be compiled with older versions, so we just
+     check that the args tuple is empty.  */
+  Py_ssize_t positional_count = PyObject_Length (args);
+  if (positional_count < 0)
+    return NULL;
+  else if (positional_count > 0)
+    {
+      /* This matches the error message that Python 3.3 raises when
+	 passing positionals to functions expecting keyword-only
+	 arguments.  */
+      PyErr_Format (PyExc_TypeError,
+		    "format_string() takes 0 positional arguments but %zu were given",
+		    positional_count);
+      return NULL;
+    }
+
+  struct value_print_options opts;
+  get_user_print_options (&opts);
+  opts.deref_ref = 0;
+
+  /* We need objects for booleans as the "p" flag for bools is new in
+     Python 3.3.  */
+  PyObject *raw_obj = NULL;
+  PyObject *pretty_arrays_obj = NULL;
+  PyObject *pretty_structs_obj = NULL;
+  PyObject *array_indexes_obj = NULL;
+  PyObject *symbols_obj = NULL;
+  PyObject *unions_obj = NULL;
+  PyObject *deref_refs_obj = NULL;
+  PyObject *actual_objects_obj = NULL;
+  PyObject *static_members_obj = NULL;
+  char *format = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (args,
+					kw,
+					"|O!O!O!O!O!O!O!O!O!IIs",
+					keywords,
+					&PyBool_Type, &raw_obj,
+					&PyBool_Type, &pretty_arrays_obj,
+					&PyBool_Type, &pretty_structs_obj,
+					&PyBool_Type, &array_indexes_obj,
+					&PyBool_Type, &symbols_obj,
+					&PyBool_Type, &unions_obj,
+					&PyBool_Type, &deref_refs_obj,
+					&PyBool_Type, &actual_objects_obj,
+					&PyBool_Type, &static_members_obj,
+					&opts.print_max,
+					&opts.repeat_count_threshold,
+					&format))
+    return NULL;
+
+  /* Set boolean arguments.  */
+  if (copy_py_bool_obj (&opts.raw, raw_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.prettyformat_arrays, pretty_arrays_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.prettyformat_structs, pretty_structs_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.print_array_indexes, array_indexes_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.symbol_print, symbols_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.unionprint, unions_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.deref_ref, deref_refs_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.objectprint, actual_objects_obj) < 0)
+    return NULL;
+  if (copy_py_bool_obj (&opts.static_field_print, static_members_obj) < 0)
+    return NULL;
+
+  /* Numeric arguments for which 0 means unlimited (which we represent as
+     UINT_MAX).  */
+  if (opts.print_max == 0)
+    opts.print_max = UINT_MAX;
+  if (opts.repeat_count_threshold == 0)
+    opts.repeat_count_threshold = UINT_MAX;
+
+  /* Other arguments.  */
+  if (format != NULL)
+    {
+      if (strlen (format) == 1)
+	opts.format = format[0];
+      else
+	{
+	  /* Mimic the message on standard Python ones for similar
+	     errors.  */
+	  PyErr_SetString (PyExc_ValueError,
+			   "a single character is required");
+	  return NULL;
+	}
+    }
+
+  string_file stb;
+
+  TRY
+    {
+      common_val_print (((value_object *) self)->value, &stb, 0,
+			&opts, python_language);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return PyUnicode_Decode (stb.c_str (), stb.size (), host_charset (), NULL);
+}
+
 /* A helper function that implements the various cast operators.  */
 
 static PyObject *
@@ -1944,6 +2100,11 @@ Return a lazy string representation of the value." },
 Return Unicode string representation of the value." },
   { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
     "Fetches the value from the inferior, if it was lazy." },
+  { "format_string", (PyCFunction) valpy_format_string,
+    METH_VARARGS | METH_KEYWORDS,
+    "format_string (...) -> string\n\
+Return a string representation of the value using the specified\n\
+formatting options" },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-format-string.c b/gdb/testsuite/gdb.python/py-format-string.c
new file mode 100644
index 0000000000..120ecce989
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.c
@@ -0,0 +1,118 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+typedef struct point
+{
+  int x;
+  int y;
+} point_t;
+
+typedef union
+{
+  int an_int;
+  char a_char;
+} union_t;
+
+typedef struct
+{
+  union_t the_union;
+} struct_union_t;
+
+typedef enum
+{
+  ENUM_FOO,
+  ENUM_BAR,
+} enum_t;
+
+typedef void (*function_t) (int);
+
+static void
+my_function(int n)
+{
+}
+
+#ifdef __cplusplus
+
+struct Base
+{
+  Base (int a_) : a (a_) {}
+
+  virtual int get_number () { return a; }
+
+  int a;
+
+  static int a_static_member;
+};
+
+int Base::a_static_member = 2019;
+
+struct Deriv : Base
+{
+  Deriv (int b_) : Base (42), b (b_) {}
+
+  virtual int get_number () { return b; }
+
+  int b;
+};
+
+#endif
+
+int global_symbol = 42;
+
+int
+main ()
+{
+  point_t a_point_t = { 42, 12 };
+  point_t *a_point_t_pointer = &a_point_t;
+#ifdef __cplusplus
+  point_t &a_point_t_ref = a_point_t;
+#endif
+  struct point another_point = { 123, 456 };
+
+  struct_union_t a_struct_with_union;
+  a_struct_with_union.the_union.an_int = 42;
+
+  enum_t an_enum = ENUM_BAR;
+
+  const char *a_string = "hello world";
+  const char *a_binary_string = "hello\0world";
+  const char a_binary_string_array[] = "hello\0world";
+
+  const int letters_repeat = 10;
+  char a_big_string[26 * letters_repeat + 1];
+  a_big_string[26 * letters_repeat] = '\0';
+  for (int i = 0; i < letters_repeat; i++)
+    for (char c = 'A'; c <= 'Z'; c++)
+      a_big_string[i * 26 + c - 'A'] = c;
+
+  int an_array[] = { 2, 3, 5 };
+
+  int an_array_with_repetition[] = {
+    1,					/*  1 time.   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 12 times.  */
+    5, 5, 5,				/*  3 times   */
+    };
+
+  int *a_symbol_pointer = &global_symbol;
+
+#ifdef __cplusplus
+  Deriv a_deriv (123);
+  Base &a_base_ref = a_deriv;
+#endif
+
+  return 0; /* break here */
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
new file mode 100644
index 0000000000..4dbe3f80fa
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -0,0 +1,958 @@
+# Copyright (C) 2009-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Value.format_string () method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [get_compiler_info c++] {
+    return -1
+}
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc prepare_gdb {exefile} {
+  global srcdir subdir srcfile testfile hex
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+  gdb_load ${exefile}
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  if ![runto_main] then {
+      perror "couldn't run to breakpoint"
+      return
+  }
+
+  # Load the pretty printer.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+  runto_bp "break here"
+}
+
+# Set breakpoint and run to that breakpoint.
+proc runto_bp {bp} {
+  gdb_breakpoint [gdb_get_line_number $bp]
+  gdb_continue_to_breakpoint $bp
+}
+
+# Set an option using the GDB command in $set_cmd, execute $body, and then
+# restore the option using the GDB command in $unset_cmd.
+proc with_temp_option { set_cmd unset_cmd body } {
+  with_test_prefix $set_cmd {
+    gdb_test "$set_cmd" ".*"
+    uplevel 1 $body
+    gdb_test "$unset_cmd" ".*"
+  }
+}
+
+# A regular expression for a pointer.
+set default_pointer_regexp "0x\[a-fA-F0-9\]+"
+
+# A regular expression for a non-expanded C++ reference.
+#
+# Stringifying a C++ reference produces an address preceeded by a "@" in
+# Python, but, by default, the C++ reference/class is expanded by the
+# GDB print command.
+set default_ref_regexp "@${default_pointer_regexp}"
+
+# The whole content of the C variable a_big_string, i.e. the whole English
+# alphabet repeated 10 times.
+set whole_big_string ""
+set alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+for {set i 0} {$i < 10} {incr i} {
+  append whole_big_string $alphabet
+}
+unset alphabet
+
+# Produces a potentially cut down version of $whole_big_string like GDB
+# would represent it.
+# $max is the maximum number of characters allowed in the string (but
+# the return value may contain more to accound for the extra quotes and
+# "..." added by GDB).
+proc get_cut_big_string { max } {
+  global whole_big_string
+
+  set whole_size [string length $whole_big_string]
+  if { $max > $whole_size } {
+    return "\"${whole_big_string}\""
+  }
+
+  set cut_string [string range $whole_big_string 0 [expr $max - 1]]
+  return "\"${cut_string}\"..."
+}
+
+# A dictionary mapping from C variable names to their default string
+# representation when using str () or gdb.Value.format_string () with
+# no arguments.
+# This usually matches what the print command prints if used with no
+# options, except for C++ references which are not expanded by
+# default in Python.  See the comment above $default_ref_regexp.
+set default_regexp_dict [dict create \
+  "a_point_t"			"Pretty Point \\(42, 12\\)" \
+  "a_point_t_pointer"		$default_pointer_regexp \
+  "a_point_t_ref"		"Pretty Point \\(42, 12\\)" \
+  "another_point"		"Pretty Point \\(123, 456\\)" \
+  "a_struct_with_union"		"\\{the_union = \\{an_int = 42, a_char = 42 '\\*'\\}\\}" \
+  "an_enum"			"ENUM_BAR" \
+  "a_string"			"${default_pointer_regexp} \"hello world\"" \
+  "a_binary_string"		"${default_pointer_regexp} \"hello\"" \
+  "a_binary_string_array"	"\"hello\\\\000world\"" \
+  "a_big_string"		[get_cut_big_string 200] \
+  "an_array"			"\\{2, 3, 5\\}" \
+  "an_array_with_repetition"	"\\{1, 3 <repeats 12 times>, 5, 5, 5\\}" \
+  "a_symbol_pointer"		"${default_pointer_regexp} <global_symbol>" \
+  "a_base_ref"			"${default_ref_regexp}" \
+  ]
+
+# A sentinel value to pass to function to get them to use a default value
+# instead.
+# Note that we cannot use $undefined for default arguments in function
+# definitions as we would just get the literal "$undefined" string, so
+# we need to repeat the string.
+set undefined "\000UNDEFINED\000"
+
+# Return $value if it's not $undefined, otherwise return the default value
+# (from $default_regexp_dict) for the variable $var.
+proc get_value_or_default { var value } {
+  global undefined
+  if { $value != $undefined } {
+    return $value
+  }
+
+  global default_regexp_dict
+  return [dict get $default_regexp_dict $var]
+}
+
+# Check that using gdb.Value.format_string on the value representing the
+# variable $var produces $expected.
+proc check_format_string {
+	var
+	opts
+	{ expected "\000UNDEFINED\000" }
+	{ name "\000UNDEFINED\000" }
+  } {
+  global undefined
+
+  set expected [get_value_or_default $var $expected]
+  if { $name == $undefined } {
+    set name "${var} with option ${opts}"
+  }
+
+  gdb_test \
+    "python print (gdb.parse_and_eval ('${var}').format_string (${opts}))" \
+    $expected \
+    $name
+}
+
+# Check that printing $var with no options set, produces the expected
+# output.
+proc check_var_with_no_opts {
+	var
+	{ expected "\000UNDEFINED\000" }
+  } {
+  set expected [get_value_or_default $var $expected]
+
+  with_test_prefix "${var}" {
+    check_format_string \
+      $var \
+      "" \
+      $expected \
+      "no opts"
+    # str () should behave like gdb.Value.format_string () with no args.
+    gdb_test \
+      "python print (str (gdb.parse_and_eval ('${var}')))" \
+      $expected \
+      "str"
+  }
+}
+
+# Check that printing $var with $opt set to True and set to False,
+# produces the expected output.
+proc check_var_with_bool_opt {
+	opt
+	var
+	{ true_expected  "\000UNDEFINED\000" }
+	{ false_expected "\000UNDEFINED\000" }
+  } {
+  set true_expected  [get_value_or_default $var $true_expected]
+  set false_expected [get_value_or_default $var $false_expected]
+
+  with_test_prefix "${var} with option ${opt}" {
+    # Option set to True.
+    check_format_string \
+      $var \
+      "${opt}=True" \
+      $true_expected \
+      "${opt}=true"
+    # Option set to False.
+    check_format_string \
+      $var \
+      "${opt}=False" \
+      $false_expected \
+      "${opt}=false"
+  }
+}
+
+# Test gdb.Value.format_string with no options.
+proc test_no_opts {} {
+  global current_lang
+
+  check_var_with_no_opts "a_point_t"
+  check_var_with_no_opts "a_point_t_pointer"
+  check_var_with_no_opts "another_point"
+  check_var_with_no_opts "a_struct_with_union"
+  check_var_with_no_opts "an_enum"
+  check_var_with_no_opts "a_string"
+  check_var_with_no_opts "a_binary_string"
+  check_var_with_no_opts "a_binary_string_array"
+  check_var_with_no_opts "a_big_string"
+  check_var_with_no_opts "an_array"
+  check_var_with_no_opts "an_array_with_repetition"
+  check_var_with_no_opts "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_no_opts "a_point_t_ref"
+    check_var_with_no_opts "a_base_ref"
+  }
+}
+
+# Test the raw option for gdb.Value.format_string.
+proc test_raw {} {
+  global current_lang
+  global default_ref_regexp
+
+  check_var_with_bool_opt "raw" "a_point_t" \
+    "{x = 42, y = 12}"
+  check_var_with_bool_opt "raw" "a_point_t_pointer"
+  check_var_with_bool_opt "raw" "another_point" \
+    "{x = 123, y = 456}"
+  check_var_with_bool_opt "raw" "a_struct_with_union"
+  check_var_with_bool_opt "raw" "an_enum"
+  check_var_with_bool_opt "raw" "a_string"
+  check_var_with_bool_opt "raw" "a_binary_string"
+  check_var_with_bool_opt "raw" "a_binary_string_array"
+  check_var_with_bool_opt "raw" "a_big_string"
+  check_var_with_bool_opt "raw" "an_array"
+  check_var_with_bool_opt "raw" "an_array_with_repetition"
+  check_var_with_bool_opt "raw" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "raw" "a_point_t_ref" \
+      ${default_ref_regexp}
+    check_var_with_bool_opt "raw" "a_base_ref"
+  }
+
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "raw" "a_point_t" \
+      "{x = 42, y = 12}" \
+      "{x = 42, y = 12}"
+  }
+}
+
+# Test the pretty_arrays option for gdb.Value.format_string.
+proc test_pretty_arrays {} {
+  global current_lang
+
+  set an_array_pretty "  \\{2,\[\r\n\]+  3,\[\r\n\]+  5\\}"
+  set an_array_with_repetition_pretty \
+    "  \\{1,\[\r\n\]+  3 <repeats 12 times>,\[\r\n\]+  5,\[\r\n\]+  5,\[\r\n\]+  5\\}"
+
+  check_var_with_bool_opt "pretty_arrays" "a_point_t"
+  check_var_with_bool_opt "pretty_arrays" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_arrays" "another_point"
+  check_var_with_bool_opt "pretty_arrays" "a_struct_with_union"
+  check_var_with_bool_opt "pretty_arrays" "an_enum"
+  check_var_with_bool_opt "pretty_arrays" "a_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_arrays" "a_big_string"
+  check_var_with_bool_opt "pretty_arrays" "an_array" \
+    $an_array_pretty
+  check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+    $an_array_with_repetition_pretty
+  check_var_with_bool_opt "pretty_arrays" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_arrays" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_arrays" "a_base_ref"
+  }
+
+  with_temp_option "set print array on" "set print array off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array" \
+      $an_array_pretty
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+  }
+}
+
+# Test the pretty_structs option for gdb.Value.format_string.
+proc test_pretty_structs {} {
+  global current_lang
+
+  set a_struct_with_union_pretty \
+    "\\{\[\r\n\]+  the_union = \\{\[\r\n\]+    an_int = 42, \[\r\n\]+    a_char = 42 '\\*'\[\r\n\]+  \\}\[\r\n\]+\\}"
+  # Note the space after the colon!                        ^ here
+
+  check_var_with_bool_opt "pretty_structs" "a_point_t"
+  check_var_with_bool_opt "pretty_structs" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_structs" "another_point"
+  check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+    $a_struct_with_union_pretty
+  check_var_with_bool_opt "pretty_structs" "an_enum"
+  check_var_with_bool_opt "pretty_structs" "a_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_structs" "a_big_string"
+  check_var_with_bool_opt "pretty_structs" "an_array"
+  check_var_with_bool_opt "pretty_structs" "an_array_with_repetition"
+  check_var_with_bool_opt "pretty_structs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_structs" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_structs" "a_base_ref"
+  }
+
+  with_temp_option "set print structs on" "set print structs off" {
+    check_var_with_no_opts "a_struct_with_union"
+    check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+      $a_struct_with_union_pretty
+  }
+
+  # point_t is usually printed through the pretty printer.
+  # Try disabling it.
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "pretty_structs" "a_point_t" \
+      "\\{\[\r\n\]+  x = 42, *\[\r\n\]+  y = 12\[\r\n\]+\\}" \
+      "{x = 42, y = 12}" \
+  }
+}
+
+# Test the array_indexes option for gdb.Value.format_string.
+proc test_array_indexes {} {
+  global current_lang
+
+  set an_array_with_indexes "\\{\\\[0\\\] = 2, \\\[1\\\] = 3, \\\[2\\\] = 5\\}"
+  set an_array_with_repetition_with_indexes \
+    "\\{\\\[0\\\] = 1, \\\[1\\\] = 3 <repeats 12 times>, \\\[13\\\] = 5, \\\[14\\\] = 5, \\\[15\\\] = 5\\}"
+
+  check_var_with_bool_opt "array_indexes" "a_point_t"
+  check_var_with_bool_opt "array_indexes" "a_point_t_pointer"
+  check_var_with_bool_opt "array_indexes" "another_point"
+  check_var_with_bool_opt "array_indexes" "a_struct_with_union"
+  check_var_with_bool_opt "array_indexes" "an_enum"
+  check_var_with_bool_opt "array_indexes" "a_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string_array"
+  check_var_with_bool_opt "array_indexes" "a_big_string"
+  check_var_with_bool_opt "array_indexes" "an_array" \
+    $an_array_with_indexes
+  check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+    $an_array_with_repetition_with_indexes
+  check_var_with_bool_opt "array_indexes" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "array_indexes" "a_point_t_ref"
+    check_var_with_bool_opt "array_indexes" "a_base_ref"
+  }
+
+  with_temp_option \
+	"set print array-indexes on" \
+	"set print array-indexes off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array" \
+      $an_array_with_indexes
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+  }
+}
+
+# Test the symbols option for gdb.Value.format_string.
+proc test_symbols {} {
+  global undefined
+  global current_lang
+  global default_pointer_regexp
+
+  check_var_with_bool_opt "symbols" "a_point_t"
+  check_var_with_bool_opt "symbols" "a_point_t_pointer"
+  check_var_with_bool_opt "symbols" "another_point"
+  check_var_with_bool_opt "symbols" "a_struct_with_union"
+  check_var_with_bool_opt "symbols" "an_enum"
+  check_var_with_bool_opt "symbols" "a_string"
+  check_var_with_bool_opt "symbols" "a_binary_string"
+  check_var_with_bool_opt "symbols" "a_binary_string_array"
+  check_var_with_bool_opt "symbols" "a_big_string"
+  check_var_with_bool_opt "symbols" "an_array"
+  check_var_with_bool_opt "symbols" "an_array_with_repetition"
+  check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+    $undefined \
+    $default_pointer_regexp
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "symbols" "a_point_t_ref"
+    check_var_with_bool_opt "symbols" "a_base_ref"
+  }
+
+  with_temp_option "set print symbol off" "set print symbol on" {
+    check_var_with_no_opts "a_symbol_pointer" \
+      $default_pointer_regexp
+    check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+      $undefined \
+      $default_pointer_regexp
+  }
+}
+
+# Test the unions option for gdb.Value.format_string.
+proc test_unions {} {
+  global undefined
+  global current_lang
+
+  check_var_with_bool_opt "unions" "a_point_t"
+  check_var_with_bool_opt "unions" "a_point_t_pointer"
+  check_var_with_bool_opt "unions" "another_point"
+  check_var_with_bool_opt "unions" "a_struct_with_union" \
+    $undefined \
+    "\\{the_union = \\{...\\}\\}"
+  check_var_with_bool_opt "unions" "an_enum"
+  check_var_with_bool_opt "unions" "a_string"
+  check_var_with_bool_opt "unions" "a_binary_string"
+  check_var_with_bool_opt "unions" "a_binary_string_array"
+  check_var_with_bool_opt "unions" "a_big_string"
+  check_var_with_bool_opt "unions" "an_array"
+  check_var_with_bool_opt "unions" "an_array_with_repetition"
+  check_var_with_bool_opt "unions" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "unions" "a_point_t_ref"
+    check_var_with_bool_opt "unions" "a_base_ref"
+  }
+
+  with_temp_option "set print union off" "set print union on" {
+    check_var_with_no_opts "a_struct_with_union" \
+      "\\{the_union = \\{...\\}\\}"
+    check_var_with_bool_opt "unions" "a_struct_with_union" \
+      $undefined \
+      "\\{the_union = \\{...\\}\\}"
+  }
+}
+
+# Test the deref_refs option for gdb.Value.format_string.
+proc test_deref_refs {} {
+  global current_lang
+  global default_pointer_regexp
+  global default_ref_regexp
+
+  check_var_with_bool_opt "deref_refs" "a_point_t"
+  check_var_with_bool_opt "deref_refs" "a_point_t_pointer"
+  check_var_with_bool_opt "deref_refs" "another_point"
+  check_var_with_bool_opt "deref_refs" "a_struct_with_union"
+  check_var_with_bool_opt "deref_refs" "an_enum"
+  check_var_with_bool_opt "deref_refs" "a_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string_array"
+  check_var_with_bool_opt "deref_refs" "a_big_string"
+  check_var_with_bool_opt "deref_refs" "an_array"
+  check_var_with_bool_opt "deref_refs" "an_array_with_repetition"
+  check_var_with_bool_opt "deref_refs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "deref_refs" "a_point_t_ref"
+    check_var_with_bool_opt "deref_refs" "a_base_ref" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42, static a_static_member = 2019\\}"
+  }
+}
+
+# Test the actual_objects option for gdb.Value.format_string.
+proc test_actual_objects {} {
+  global current_lang
+
+  check_var_with_bool_opt "actual_objects" "a_point_t"
+  check_var_with_bool_opt "actual_objects" "a_point_t_pointer"
+  check_var_with_bool_opt "actual_objects" "another_point"
+  check_var_with_bool_opt "actual_objects" "a_struct_with_union"
+  check_var_with_bool_opt "actual_objects" "an_enum"
+  check_var_with_bool_opt "actual_objects" "a_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string_array"
+  check_var_with_bool_opt "actual_objects" "a_big_string"
+  check_var_with_bool_opt "actual_objects" "an_array"
+  check_var_with_bool_opt "actual_objects" "an_array_with_repetition"
+  check_var_with_bool_opt "actual_objects" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+    check_var_with_bool_opt "actual_objects" "a_base_ref"
+
+    with_temp_option "set print object on" "set print object off" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "actual_objects" "a_base_ref"
+    }
+  }
+}
+
+# Test the static_members option for gdb.Value.format_string.
+proc test_static_members {} {
+  global current_lang
+
+  check_var_with_bool_opt "static_members" "a_point_t"
+  check_var_with_bool_opt "static_members" "a_point_t_pointer"
+  check_var_with_bool_opt "static_members" "another_point"
+  check_var_with_bool_opt "static_members" "a_struct_with_union"
+  check_var_with_bool_opt "static_members" "an_enum"
+  check_var_with_bool_opt "static_members" "a_string"
+  check_var_with_bool_opt "static_members" "a_binary_string"
+  check_var_with_bool_opt "static_members" "a_binary_string_array"
+  check_var_with_bool_opt "static_members" "a_big_string"
+  check_var_with_bool_opt "static_members" "an_array"
+  check_var_with_bool_opt "static_members" "an_array_with_repetition"
+  check_var_with_bool_opt "static_members" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "static_members" "a_point_t_ref"
+    check_var_with_bool_opt "static_members" "a_base_ref"
+
+    with_temp_option \
+	"set print static-members off" \
+	"set print static-members on" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "static_members" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "static_members" "a_base_ref"
+    }
+  }
+}
+
+# Test the max_elements option for gdb.Value.format_string.
+proc test_max_elements {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 200 is the default maximum number of elements, so setting it should
+  # not change the output.
+  set opts "max_elements=200"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "max_elements=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    # This will print four characters instead of three, see
+    # <https://sourceware.org/bugzilla/show_bug.cgi?id=24331>.
+    check_format_string "a_binary_string_array" $opts \
+      "\"hell\"..."
+    check_format_string "a_big_string" $opts \
+      [get_cut_big_string 3]
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>...\\}"
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # mean no truncation.
+  foreach opts { "max_elements=1000" "max_elements=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts \
+        [get_cut_big_string 1000]
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print elements 4" "set print elements 200" {
+    check_format_string "a_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string_array" "" \
+      "\"hell\"..."
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>...\\}"
+  }
+}
+
+# Test the repeat_threshold option for gdb.Value.format_string.
+proc test_repeat_threshold {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 10 is the default threshold for repeated items, so setting it should
+  # not change the output.
+  set opts "repeat_threshold=10"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=1"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o world\""
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o\""
+    check_format_string "a_binary_string_array" $opts \
+      "\"he\", 'l' <repeats 2 times>, \"o\\\\000world\""
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 100 (we don't have that many repeated elements) and 0 (unlimited)
+  # should mean no truncation.
+  foreach opts { "repeat_threshold=100" "repeat_threshold=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+        "\\{1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5\\}"
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print repeats 1" "set print repeats 10" {
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+  }
+}
+
+# Test the format option for gdb.Value.format_string.
+proc test_format {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # Hexadecimal.
+  set opts "format='x'"
+  with_test_prefix $opts {
+    gdb_test "python print (gdb.Value (42).format_string (${opts}))" \
+      "0x2a" \
+      "42 with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 0x2a, a_char = 0x2a\\}\\}"
+    check_format_string "an_enum" $opts \
+      "0x1"
+    check_format_string "a_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{0x41, 0x42, 0x43, 0x44, 0x45, \[, x0-9a-f\]+\.\.\.\\}"
+    check_format_string "an_array" $opts \
+      "\\{0x2, 0x3, 0x5\\}"
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{0x1, 0x3 <repeats 12 times>, 0x5, 0x5, 0x5\\}"
+    check_format_string "a_symbol_pointer" $opts \
+      $default_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Decimal.
+  set opts "format='d'"
+  with_test_prefix $opts {
+    set decimal_pointer_regexp "\[0-9\]+"
+    gdb_test "python print (gdb.Value (0x2a).format_string (${opts}))" \
+      "42" \
+      "0x2a with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts \
+      $decimal_pointer_regexp
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 42, a_char = 42\\}\\}"
+    check_format_string "an_enum" $opts \
+      "1"
+    check_format_string "a_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{104, 101, 108, 108, 111, 0, 119, 111, 114, 108, 100, 0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{65, 66, 67, 68, 69, \[, 0-9\]+\.\.\.\\}"
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts \
+      $decimal_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+}
+
+# Test mixing options.
+proc test_mixed {} {
+  global current_lang
+  global default_ref_regexp
+  global default_pointer_regexp
+
+  check_format_string "a_point_t" \
+    "raw=True, format='x'" \
+    "\\{x = 0x2a, y = 0xc\\}"
+
+  check_format_string "an_array" \
+    "array_indexes=True, pretty_arrays=True" \
+    "  \\{\\\[0\\\] = 2,\[\r\n\]+  \\\[1\\\] = 3,\[\r\n\]+  \\\[2\\\] = 5\\}"
+
+  check_format_string "a_struct_with_union" \
+    "pretty_structs=True, unions=False" \
+    "\\{\[\r\n\]+  the_union = \\{\.\.\.\\}\[\r\n\]+\\}"
+
+  check_format_string "a_symbol_pointer" \
+    "symbols=False, format='d'" \
+    "\[0-9\]+"
+
+  if { $current_lang == "c++" } {
+    check_format_string "a_point_t_ref" \
+      "deref_refs=True, actual_objects=True, raw=True" \
+      "${default_ref_regexp}: \\{x = 42, y = 12\\}"
+
+    check_format_string "a_base_ref" \
+      "deref_refs=True, static_members=False" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42\\}"
+  }
+}
+
+# Test passing invalid arguments to gdb.Value.format_string.
+proc test_invalid_args {} {
+  check_format_string \
+    "a_point_t" \
+    "12" \
+    "TypeError: format_string\\(\\) takes 0 positional arguments but 1 were given.*"
+
+  check_format_string \
+    "a_point_t" \
+    "invalid=True" \
+    "TypeError: 'invalid' is an invalid keyword argument for this function.*"
+
+  check_format_string \
+    "a_point_t" \
+    "raw='hello'" \
+    "TypeError: argument 1 must be bool, not str.*"
+
+  check_format_string \
+    "a_point_t" \
+    "format='xd'" \
+    "ValueError: a single character is required.*"
+}
+
+# Run all the tests in common for both C and C++.
+proc test_all_common {} {
+  # No options.
+  test_no_opts
+  # Single options set to True/False.
+  test_raw
+  test_pretty_arrays
+  test_pretty_structs
+  test_array_indexes
+  test_symbols
+  test_unions
+  test_deref_refs
+  test_actual_objects
+  test_static_members
+  test_max_elements
+  test_repeat_threshold
+  test_format
+  # Multiple options mixed together.
+  test_mixed
+  # Various error conditions.
+  test_invalid_args
+}
+
+# The current language ("c" or "c++" while running tests).
+set current_lang ""
+
+with_test_prefix "format_string" {
+  # Perform C Tests.
+  if { [build_inferior "${binfile}" "c"] == 0 } {
+    with_test_prefix "lang_c" {
+      set current_lang "c"
+      prepare_gdb "${binfile}"
+      test_all_common
+    }
+  }
+
+  # Perform C++ Tests.
+  if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
+    with_test_prefix "lang_cpp" {
+      set current_lang "c++"
+      prepare_gdb "${binfile}-cxx"
+      test_all_common
+    }
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/gdb.python/py-format-string.py
new file mode 100644
index 0000000000..c2ad88e862
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2008-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests python pretty
+# printers.
+
+import gdb
+
+class PointPrinter (object):
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return 'Pretty Point (%s, %s)' % (self.val['x'], self.val['y'])
+
+def test_lookup_function (val):
+    "Look-up and return a pretty-printer that can print val."
+
+    # Get the type.
+    type = val.type
+
+    # If it points to a reference, get the reference.
+    if type.code == gdb.TYPE_CODE_REF:
+        type = type.target ()
+
+    # Get the unqualified type, stripped of typedefs.
+    type = type.unqualified ().strip_typedefs ()
+
+    # Get the type name.
+    typename = type.tag
+
+    if typename == 'point':
+        return PointPrinter (val)
+
+    return None
+
+gdb.pretty_printers.append (test_lookup_function)
-- 
2.19.2


[-- Attachment #3: Type: text/plain, Size: 24 bytes --]




-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add gdb.Value.format_string ()
  2019-03-16 11:18 [PATCH] Add gdb.Value.format_string () Marco Barisione
@ 2019-03-17 20:50 ` Kevin Buettner
  2019-03-17 23:41   ` Marco Barisione
  2019-03-18  9:22   ` [PATCH v2] " Marco Barisione
  0 siblings, 2 replies; 16+ messages in thread
From: Kevin Buettner @ 2019-03-17 20:50 UTC (permalink / raw)
  To: gdb-patches; +Cc: Marco Barisione

Hi Marco,

Overall, I like this patch.  See below for my comments.

Kevin

On Sat, 16 Mar 2019 11:18:36 +0000
Marco Barisione <mbarisione@undo.io> wrote:

> gdb/doc/ChangeLog:
> 
> 2019-03-16  Marco Barisione  <mbarisione@undo.io>
> 
> 	* python.texi: Document gdb.Value.format_string ()

A period is needed at the end of this ChangeLog entry.

> diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
> index dd6a536b6a..46a0f563e4 100644
> --- a/gdb/python/py-value.c
> +++ b/gdb/python/py-value.c
> @@ -588,6 +588,162 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
>  			   encoding, errors);
>  }
>  
> +/* Given a Python object, copy its truth value to a C int (the value
> +   pointed by dest).
> +   If src_obj is NULL, then *dest is not modified.
> +
> +   Return 1 in case of success, -1 otherwise.  */

Why not use a bool return type to indicate success or failure?

> +
> +static int
> +copy_py_bool_obj (int *dest, PyObject *src_obj)
> +{
> +  if (src_obj)
> +    {
> +      int cmp = PyObject_IsTrue (src_obj);
> +      if (cmp < 0)
> +	return -1;
> +      *dest = cmp;
> +    }
> +
> +  return 1;
> +}

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add gdb.Value.format_string ()
  2019-03-17 20:50 ` Kevin Buettner
@ 2019-03-17 23:41   ` Marco Barisione
  2019-03-18  9:22   ` [PATCH v2] " Marco Barisione
  1 sibling, 0 replies; 16+ messages in thread
From: Marco Barisione @ 2019-03-17 23:41 UTC (permalink / raw)
  To: gdb-patches


> On 17 Mar 2019, at 20:50, Kevin Buettner <kevinb@redhat.com> wrote:
> 
> Hi Marco,
> 
> Overall, I like this patch.  See below for my comments.

Thanks!
I will fix your review comments tomorrow.

>> +/* Given a Python object, copy its truth value to a C int (the value
>> +   pointed by dest).
>> +   If src_obj is NULL, then *dest is not modified.
>> +
>> +   Return 1 in case of success, -1 otherwise.  */
> 
> Why not use a bool return type to indicate success or failure?

Because I was under the mistaken impression that this was the style for
GDB code, at least by looking at py-value.c and the other couple of files
I checked. (To be fair, the other functions in this file return 1, 0 and
-1 for errors, not just two values like mine.)

-- 
Marco Barisione

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2] Add gdb.Value.format_string ()
  2019-03-17 20:50 ` Kevin Buettner
  2019-03-17 23:41   ` Marco Barisione
@ 2019-03-18  9:22   ` Marco Barisione
  2019-03-18 16:34     ` Kevin Buettner
  2019-03-18 17:49     ` [PATCH v2] " Eli Zaretskii
  1 sibling, 2 replies; 16+ messages in thread
From: Marco Barisione @ 2019-03-18  9:22 UTC (permalink / raw)
  To: gdb-patches; +Cc: Kevin Buettner

[-- Attachment #1: Type: text/plain, Size: 329 bytes --]

Hello,

Here’s my updated patch fixing Kevin’s review comments.

Thanks again for your review!


> On 17 Mar 2019, at 20:50, Kevin Buettner <kevinb@redhat.com> wrote:
> A period is needed at the end of this ChangeLog entry.

Fixed.

> Why not use a bool return type to indicate success or failure?

Fixed.



[-- Attachment #2: 0001-Add-gdb.Value.format_string.patch --]
[-- Type: application/octet-stream, Size: 53028 bytes --]

From 004d2c324676852d55e3d466ac78f76deaa0ee55 Mon Sep 17 00:00:00 2001
From: Marco Barisione <mbarisione@undo.io>
Date: Mon, 18 Mar 2019 09:15:13 +0000
Subject: [PATCH] Add gdb.Value.format_string ()

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

This patch introduces a new format_string () method to gdb.Value which
allows specifying more formatting options, thus giving access to more
features provided by the internal C function common_val_print ().

gdb/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Add gdb.Value.format_string ().
	* python/py-value.c (copy_py_bool_obj):
	(valpy_format_string): Add gdb.Value.format_string ().
	* NEWS: Document the addition of gdb.Value.format_string ().

gdb/doc/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	* python.texi: Document gdb.Value.format_string ().

gdb/testsuite/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Test gdb.Value.format_string ().
	* gdb.python/py-format-string.exp: New test.
	* gdb.python/py-format-string.c: New file.
	* gdb.python/py-format-string.py: New file.
---
 gdb/NEWS                                      |   6 +
 gdb/doc/python.texi                           |  79 ++
 gdb/python/py-value.c                         | 162 +++
 gdb/testsuite/gdb.python/py-format-string.c   | 118 +++
 gdb/testsuite/gdb.python/py-format-string.exp | 958 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-format-string.py  |  49 +
 6 files changed, 1372 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.c
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.exp
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.py

diff --git a/gdb/NEWS b/gdb/NEWS
index c45b313406..e4d9ae0fea 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -231,6 +231,12 @@ before Windows XP.
   ** The gdb.Value type has a new constructor, which is used to construct a
      gdb.Value from a Python buffer object and a gdb.Type.
 
+  ** The gdb.Value type has a new method 'format_string' which returns a
+     string representing the value.  The formatting is controlled by the
+     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
+     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
+     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
+
 * Configure changes
 
 --enable-ubsan
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index b02a154ce1..71d6d344fb 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -864,6 +864,85 @@ Like @code{Value.cast}, but works as if the C@t{++} @code{reinterpret_cast}
 operator were used.  Consult a C@t{++} reference for details.
 @end defun
 
+@defun Value.format_string (...)
+Convert a @code{gdb.Value} to a string, similarly to what the @code{print}
+command does.  Invoked with no arguments, this is equivalent to calling
+the @code{str} function on the @code{gdb.Value}.  The representation of
+the same value may change across different versions of @value{GDBN}, so
+you shouldn't, for instance, parse the strings returned by this method.
+
+All the arguments are keyword only.  If an argument is not specified, the
+current global default setting is used.
+
+@table @code
+@item raw
+@code{True} if pretty-printers (@pxref{Pretty Printing}) should not be
+used to format the value.  @code{False} if enabled pretty-printers
+matching the type represented by the @code{gdb.Value} should be used to
+format it.
+
+@item pretty_arrays
+@code{True} if arrays should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print array} in
+@ref{Print Settings}).
+
+@item pretty_structs
+@code{True} if structs should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print pretty} in
+@ref{Print Settings}).
+
+@item array_indexes
+@code{True} if array indexes should be included in the string
+representation of arrays, @code{False} if they shouldn't (see @code{set
+print array-indexes} in @ref{Print Settings}).
+
+@item symbols
+@code{True} if the string representation of a pointer should include the
+corresponding symbol name (if one exists), @code{False} if it shouldn't
+(see @code{set print symbol} in @ref{Print Settings}).
+
+@item unions
+@code{True} if unions which are contained in other structures or unions
+should be expanded, @code{False} if they shouldn't (see @code{set print
+union} in @ref{Print Settings}).
+
+@item deref_refs
+@code{True} if C++ references should be resolved to the value they refer
+to, @code{False} (the default) if they shouldn't.  Note that, unlike for
+the @code{print} command, references are not automatically expanded when
+using the @code{format_string ()} method or the @code{str ()} function.
+There is no global @code{print} setting to change the default behaviour.
+
+@item actual_objects
+@code{True} if the representation of a pointer to an object should
+identify the @emph{actual} (derived) type of the object rather than the
+@emph{declared} type, using the virtual function table.  @code{False} if
+the @emph{declared} type should be used.  (See @code{set print object} in
+@ref{Print Settings}).
+
+@item static_fields
+@code{True} if static members should be included in the string
+representation of a C++ object, @code{False} if they shouldn't (see
+@code{set print static-members} in @ref{Print Settings}).
+
+@item max_elements
+Number of array elements to print, or @code{0} to print an unlimited
+number of elements (see @code{set print elements} in @ref{Print
+Settings}).
+
+@item repeat_threshold
+Set the threshold for suppressing display of repeated array elements, or
+@code{0} to represent all elements, even if repeated.  (See @code{set
+print repeats} in @ref{Print Settings}).
+
+@item format
+A string containing a single character representing the format to use for
+the returned string.  For instance, @code{'x'} is equivalent to using the
+@value{GDBN} command @code{print} with the @code{/x} option and formats
+the value as a hexadecimal number.
+@end table
+@end defun
+
 @defun Value.string (@r{[}encoding@r{[}, errors@r{[}, length@r{]]]})
 If this @code{gdb.Value} represents a string, then this method
 converts the contents to a Python string.  Otherwise, this method will
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index dd6a536b6a..17f85d97d5 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -588,6 +588,163 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
 			   encoding, errors);
 }
 
+/* Given a Python object, copy its truth value to a C int (the value
+   pointed by dest).
+   If src_obj is NULL, then *dest is not modified.
+
+   Return true in case of success (including src_obj being NULL), false
+   in case of error.  */
+
+static bool
+copy_py_bool_obj (int *dest, PyObject *src_obj)
+{
+  if (src_obj)
+    {
+      int cmp = PyObject_IsTrue (src_obj);
+      if (cmp < 0)
+	return false;
+      *dest = cmp;
+    }
+
+  return true;
+}
+
+/* Implementation of gdb.Value.format_string (...) -> string.
+   Return Unicode string with value contents formatted using the
+   keyword-only arguments.  */
+
+static PyObject *
+valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = {
+      /* Basic C/C++ options.  */
+      "raw",			/* See the /r option to print.  */
+      "pretty_arrays",		/* See set print array on|off.  */
+      "pretty_structs",		/* See set print pretty on|off.  */
+      "array_indexes",		/* See set print array-indexes on|off.  */
+      "symbols",		/* See set print symbol on|off.  */
+      "unions",			/* See set print union on|off.  */
+      /* C++ options.  */
+      "deref_refs",		/* No corresponding setting.  */
+      "actual_objects",		/* See set print object on|off.  */
+      "static_members",		/* See set print static-members on|off.  */
+      /* C non-bool options.  */
+      "max_elements", 		/* See set print elements N.  */
+      "repeat_threshold",	/* See set print repeats.  */
+      "format",			/* The format passed to the print command.  */
+      NULL };
+
+  /* This function has too many arguments to be useful as positionals, so
+     the user should specify them all as keyword arguments.
+     Python 3.3 and later have a way to specify it (both in C and Python
+     itself), but we could be compiled with older versions, so we just
+     check that the args tuple is empty.  */
+  Py_ssize_t positional_count = PyObject_Length (args);
+  if (positional_count < 0)
+    return NULL;
+  else if (positional_count > 0)
+    {
+      /* This matches the error message that Python 3.3 raises when
+	 passing positionals to functions expecting keyword-only
+	 arguments.  */
+      PyErr_Format (PyExc_TypeError,
+		    "format_string() takes 0 positional arguments but %zu were given",
+		    positional_count);
+      return NULL;
+    }
+
+  struct value_print_options opts;
+  get_user_print_options (&opts);
+  opts.deref_ref = 0;
+
+  /* We need objects for booleans as the "p" flag for bools is new in
+     Python 3.3.  */
+  PyObject *raw_obj = NULL;
+  PyObject *pretty_arrays_obj = NULL;
+  PyObject *pretty_structs_obj = NULL;
+  PyObject *array_indexes_obj = NULL;
+  PyObject *symbols_obj = NULL;
+  PyObject *unions_obj = NULL;
+  PyObject *deref_refs_obj = NULL;
+  PyObject *actual_objects_obj = NULL;
+  PyObject *static_members_obj = NULL;
+  char *format = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (args,
+					kw,
+					"|O!O!O!O!O!O!O!O!O!IIs",
+					keywords,
+					&PyBool_Type, &raw_obj,
+					&PyBool_Type, &pretty_arrays_obj,
+					&PyBool_Type, &pretty_structs_obj,
+					&PyBool_Type, &array_indexes_obj,
+					&PyBool_Type, &symbols_obj,
+					&PyBool_Type, &unions_obj,
+					&PyBool_Type, &deref_refs_obj,
+					&PyBool_Type, &actual_objects_obj,
+					&PyBool_Type, &static_members_obj,
+					&opts.print_max,
+					&opts.repeat_count_threshold,
+					&format))
+    return NULL;
+
+  /* Set boolean arguments.  */
+  if (!copy_py_bool_obj (&opts.raw, raw_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_arrays, pretty_arrays_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_structs, pretty_structs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.print_array_indexes, array_indexes_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.symbol_print, symbols_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.unionprint, unions_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.deref_ref, deref_refs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.objectprint, actual_objects_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.static_field_print, static_members_obj))
+    return NULL;
+
+  /* Numeric arguments for which 0 means unlimited (which we represent as
+     UINT_MAX).  */
+  if (opts.print_max == 0)
+    opts.print_max = UINT_MAX;
+  if (opts.repeat_count_threshold == 0)
+    opts.repeat_count_threshold = UINT_MAX;
+
+  /* Other arguments.  */
+  if (format != NULL)
+    {
+      if (strlen (format) == 1)
+	opts.format = format[0];
+      else
+	{
+	  /* Mimic the message on standard Python ones for similar
+	     errors.  */
+	  PyErr_SetString (PyExc_ValueError,
+			   "a single character is required");
+	  return NULL;
+	}
+    }
+
+  string_file stb;
+
+  TRY
+    {
+      common_val_print (((value_object *) self)->value, &stb, 0,
+			&opts, python_language);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return PyUnicode_Decode (stb.c_str (), stb.size (), host_charset (), NULL);
+}
+
 /* A helper function that implements the various cast operators.  */
 
 static PyObject *
@@ -1944,6 +2101,11 @@ Return a lazy string representation of the value." },
 Return Unicode string representation of the value." },
   { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
     "Fetches the value from the inferior, if it was lazy." },
+  { "format_string", (PyCFunction) valpy_format_string,
+    METH_VARARGS | METH_KEYWORDS,
+    "format_string (...) -> string\n\
+Return a string representation of the value using the specified\n\
+formatting options" },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-format-string.c b/gdb/testsuite/gdb.python/py-format-string.c
new file mode 100644
index 0000000000..120ecce989
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.c
@@ -0,0 +1,118 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+typedef struct point
+{
+  int x;
+  int y;
+} point_t;
+
+typedef union
+{
+  int an_int;
+  char a_char;
+} union_t;
+
+typedef struct
+{
+  union_t the_union;
+} struct_union_t;
+
+typedef enum
+{
+  ENUM_FOO,
+  ENUM_BAR,
+} enum_t;
+
+typedef void (*function_t) (int);
+
+static void
+my_function(int n)
+{
+}
+
+#ifdef __cplusplus
+
+struct Base
+{
+  Base (int a_) : a (a_) {}
+
+  virtual int get_number () { return a; }
+
+  int a;
+
+  static int a_static_member;
+};
+
+int Base::a_static_member = 2019;
+
+struct Deriv : Base
+{
+  Deriv (int b_) : Base (42), b (b_) {}
+
+  virtual int get_number () { return b; }
+
+  int b;
+};
+
+#endif
+
+int global_symbol = 42;
+
+int
+main ()
+{
+  point_t a_point_t = { 42, 12 };
+  point_t *a_point_t_pointer = &a_point_t;
+#ifdef __cplusplus
+  point_t &a_point_t_ref = a_point_t;
+#endif
+  struct point another_point = { 123, 456 };
+
+  struct_union_t a_struct_with_union;
+  a_struct_with_union.the_union.an_int = 42;
+
+  enum_t an_enum = ENUM_BAR;
+
+  const char *a_string = "hello world";
+  const char *a_binary_string = "hello\0world";
+  const char a_binary_string_array[] = "hello\0world";
+
+  const int letters_repeat = 10;
+  char a_big_string[26 * letters_repeat + 1];
+  a_big_string[26 * letters_repeat] = '\0';
+  for (int i = 0; i < letters_repeat; i++)
+    for (char c = 'A'; c <= 'Z'; c++)
+      a_big_string[i * 26 + c - 'A'] = c;
+
+  int an_array[] = { 2, 3, 5 };
+
+  int an_array_with_repetition[] = {
+    1,					/*  1 time.   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 12 times.  */
+    5, 5, 5,				/*  3 times   */
+    };
+
+  int *a_symbol_pointer = &global_symbol;
+
+#ifdef __cplusplus
+  Deriv a_deriv (123);
+  Base &a_base_ref = a_deriv;
+#endif
+
+  return 0; /* break here */
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
new file mode 100644
index 0000000000..4dbe3f80fa
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -0,0 +1,958 @@
+# Copyright (C) 2009-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Value.format_string () method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [get_compiler_info c++] {
+    return -1
+}
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc prepare_gdb {exefile} {
+  global srcdir subdir srcfile testfile hex
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+  gdb_load ${exefile}
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  if ![runto_main] then {
+      perror "couldn't run to breakpoint"
+      return
+  }
+
+  # Load the pretty printer.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+  runto_bp "break here"
+}
+
+# Set breakpoint and run to that breakpoint.
+proc runto_bp {bp} {
+  gdb_breakpoint [gdb_get_line_number $bp]
+  gdb_continue_to_breakpoint $bp
+}
+
+# Set an option using the GDB command in $set_cmd, execute $body, and then
+# restore the option using the GDB command in $unset_cmd.
+proc with_temp_option { set_cmd unset_cmd body } {
+  with_test_prefix $set_cmd {
+    gdb_test "$set_cmd" ".*"
+    uplevel 1 $body
+    gdb_test "$unset_cmd" ".*"
+  }
+}
+
+# A regular expression for a pointer.
+set default_pointer_regexp "0x\[a-fA-F0-9\]+"
+
+# A regular expression for a non-expanded C++ reference.
+#
+# Stringifying a C++ reference produces an address preceeded by a "@" in
+# Python, but, by default, the C++ reference/class is expanded by the
+# GDB print command.
+set default_ref_regexp "@${default_pointer_regexp}"
+
+# The whole content of the C variable a_big_string, i.e. the whole English
+# alphabet repeated 10 times.
+set whole_big_string ""
+set alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+for {set i 0} {$i < 10} {incr i} {
+  append whole_big_string $alphabet
+}
+unset alphabet
+
+# Produces a potentially cut down version of $whole_big_string like GDB
+# would represent it.
+# $max is the maximum number of characters allowed in the string (but
+# the return value may contain more to accound for the extra quotes and
+# "..." added by GDB).
+proc get_cut_big_string { max } {
+  global whole_big_string
+
+  set whole_size [string length $whole_big_string]
+  if { $max > $whole_size } {
+    return "\"${whole_big_string}\""
+  }
+
+  set cut_string [string range $whole_big_string 0 [expr $max - 1]]
+  return "\"${cut_string}\"..."
+}
+
+# A dictionary mapping from C variable names to their default string
+# representation when using str () or gdb.Value.format_string () with
+# no arguments.
+# This usually matches what the print command prints if used with no
+# options, except for C++ references which are not expanded by
+# default in Python.  See the comment above $default_ref_regexp.
+set default_regexp_dict [dict create \
+  "a_point_t"			"Pretty Point \\(42, 12\\)" \
+  "a_point_t_pointer"		$default_pointer_regexp \
+  "a_point_t_ref"		"Pretty Point \\(42, 12\\)" \
+  "another_point"		"Pretty Point \\(123, 456\\)" \
+  "a_struct_with_union"		"\\{the_union = \\{an_int = 42, a_char = 42 '\\*'\\}\\}" \
+  "an_enum"			"ENUM_BAR" \
+  "a_string"			"${default_pointer_regexp} \"hello world\"" \
+  "a_binary_string"		"${default_pointer_regexp} \"hello\"" \
+  "a_binary_string_array"	"\"hello\\\\000world\"" \
+  "a_big_string"		[get_cut_big_string 200] \
+  "an_array"			"\\{2, 3, 5\\}" \
+  "an_array_with_repetition"	"\\{1, 3 <repeats 12 times>, 5, 5, 5\\}" \
+  "a_symbol_pointer"		"${default_pointer_regexp} <global_symbol>" \
+  "a_base_ref"			"${default_ref_regexp}" \
+  ]
+
+# A sentinel value to pass to function to get them to use a default value
+# instead.
+# Note that we cannot use $undefined for default arguments in function
+# definitions as we would just get the literal "$undefined" string, so
+# we need to repeat the string.
+set undefined "\000UNDEFINED\000"
+
+# Return $value if it's not $undefined, otherwise return the default value
+# (from $default_regexp_dict) for the variable $var.
+proc get_value_or_default { var value } {
+  global undefined
+  if { $value != $undefined } {
+    return $value
+  }
+
+  global default_regexp_dict
+  return [dict get $default_regexp_dict $var]
+}
+
+# Check that using gdb.Value.format_string on the value representing the
+# variable $var produces $expected.
+proc check_format_string {
+	var
+	opts
+	{ expected "\000UNDEFINED\000" }
+	{ name "\000UNDEFINED\000" }
+  } {
+  global undefined
+
+  set expected [get_value_or_default $var $expected]
+  if { $name == $undefined } {
+    set name "${var} with option ${opts}"
+  }
+
+  gdb_test \
+    "python print (gdb.parse_and_eval ('${var}').format_string (${opts}))" \
+    $expected \
+    $name
+}
+
+# Check that printing $var with no options set, produces the expected
+# output.
+proc check_var_with_no_opts {
+	var
+	{ expected "\000UNDEFINED\000" }
+  } {
+  set expected [get_value_or_default $var $expected]
+
+  with_test_prefix "${var}" {
+    check_format_string \
+      $var \
+      "" \
+      $expected \
+      "no opts"
+    # str () should behave like gdb.Value.format_string () with no args.
+    gdb_test \
+      "python print (str (gdb.parse_and_eval ('${var}')))" \
+      $expected \
+      "str"
+  }
+}
+
+# Check that printing $var with $opt set to True and set to False,
+# produces the expected output.
+proc check_var_with_bool_opt {
+	opt
+	var
+	{ true_expected  "\000UNDEFINED\000" }
+	{ false_expected "\000UNDEFINED\000" }
+  } {
+  set true_expected  [get_value_or_default $var $true_expected]
+  set false_expected [get_value_or_default $var $false_expected]
+
+  with_test_prefix "${var} with option ${opt}" {
+    # Option set to True.
+    check_format_string \
+      $var \
+      "${opt}=True" \
+      $true_expected \
+      "${opt}=true"
+    # Option set to False.
+    check_format_string \
+      $var \
+      "${opt}=False" \
+      $false_expected \
+      "${opt}=false"
+  }
+}
+
+# Test gdb.Value.format_string with no options.
+proc test_no_opts {} {
+  global current_lang
+
+  check_var_with_no_opts "a_point_t"
+  check_var_with_no_opts "a_point_t_pointer"
+  check_var_with_no_opts "another_point"
+  check_var_with_no_opts "a_struct_with_union"
+  check_var_with_no_opts "an_enum"
+  check_var_with_no_opts "a_string"
+  check_var_with_no_opts "a_binary_string"
+  check_var_with_no_opts "a_binary_string_array"
+  check_var_with_no_opts "a_big_string"
+  check_var_with_no_opts "an_array"
+  check_var_with_no_opts "an_array_with_repetition"
+  check_var_with_no_opts "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_no_opts "a_point_t_ref"
+    check_var_with_no_opts "a_base_ref"
+  }
+}
+
+# Test the raw option for gdb.Value.format_string.
+proc test_raw {} {
+  global current_lang
+  global default_ref_regexp
+
+  check_var_with_bool_opt "raw" "a_point_t" \
+    "{x = 42, y = 12}"
+  check_var_with_bool_opt "raw" "a_point_t_pointer"
+  check_var_with_bool_opt "raw" "another_point" \
+    "{x = 123, y = 456}"
+  check_var_with_bool_opt "raw" "a_struct_with_union"
+  check_var_with_bool_opt "raw" "an_enum"
+  check_var_with_bool_opt "raw" "a_string"
+  check_var_with_bool_opt "raw" "a_binary_string"
+  check_var_with_bool_opt "raw" "a_binary_string_array"
+  check_var_with_bool_opt "raw" "a_big_string"
+  check_var_with_bool_opt "raw" "an_array"
+  check_var_with_bool_opt "raw" "an_array_with_repetition"
+  check_var_with_bool_opt "raw" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "raw" "a_point_t_ref" \
+      ${default_ref_regexp}
+    check_var_with_bool_opt "raw" "a_base_ref"
+  }
+
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "raw" "a_point_t" \
+      "{x = 42, y = 12}" \
+      "{x = 42, y = 12}"
+  }
+}
+
+# Test the pretty_arrays option for gdb.Value.format_string.
+proc test_pretty_arrays {} {
+  global current_lang
+
+  set an_array_pretty "  \\{2,\[\r\n\]+  3,\[\r\n\]+  5\\}"
+  set an_array_with_repetition_pretty \
+    "  \\{1,\[\r\n\]+  3 <repeats 12 times>,\[\r\n\]+  5,\[\r\n\]+  5,\[\r\n\]+  5\\}"
+
+  check_var_with_bool_opt "pretty_arrays" "a_point_t"
+  check_var_with_bool_opt "pretty_arrays" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_arrays" "another_point"
+  check_var_with_bool_opt "pretty_arrays" "a_struct_with_union"
+  check_var_with_bool_opt "pretty_arrays" "an_enum"
+  check_var_with_bool_opt "pretty_arrays" "a_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_arrays" "a_big_string"
+  check_var_with_bool_opt "pretty_arrays" "an_array" \
+    $an_array_pretty
+  check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+    $an_array_with_repetition_pretty
+  check_var_with_bool_opt "pretty_arrays" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_arrays" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_arrays" "a_base_ref"
+  }
+
+  with_temp_option "set print array on" "set print array off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array" \
+      $an_array_pretty
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+  }
+}
+
+# Test the pretty_structs option for gdb.Value.format_string.
+proc test_pretty_structs {} {
+  global current_lang
+
+  set a_struct_with_union_pretty \
+    "\\{\[\r\n\]+  the_union = \\{\[\r\n\]+    an_int = 42, \[\r\n\]+    a_char = 42 '\\*'\[\r\n\]+  \\}\[\r\n\]+\\}"
+  # Note the space after the colon!                        ^ here
+
+  check_var_with_bool_opt "pretty_structs" "a_point_t"
+  check_var_with_bool_opt "pretty_structs" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_structs" "another_point"
+  check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+    $a_struct_with_union_pretty
+  check_var_with_bool_opt "pretty_structs" "an_enum"
+  check_var_with_bool_opt "pretty_structs" "a_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_structs" "a_big_string"
+  check_var_with_bool_opt "pretty_structs" "an_array"
+  check_var_with_bool_opt "pretty_structs" "an_array_with_repetition"
+  check_var_with_bool_opt "pretty_structs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_structs" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_structs" "a_base_ref"
+  }
+
+  with_temp_option "set print structs on" "set print structs off" {
+    check_var_with_no_opts "a_struct_with_union"
+    check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+      $a_struct_with_union_pretty
+  }
+
+  # point_t is usually printed through the pretty printer.
+  # Try disabling it.
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "pretty_structs" "a_point_t" \
+      "\\{\[\r\n\]+  x = 42, *\[\r\n\]+  y = 12\[\r\n\]+\\}" \
+      "{x = 42, y = 12}" \
+  }
+}
+
+# Test the array_indexes option for gdb.Value.format_string.
+proc test_array_indexes {} {
+  global current_lang
+
+  set an_array_with_indexes "\\{\\\[0\\\] = 2, \\\[1\\\] = 3, \\\[2\\\] = 5\\}"
+  set an_array_with_repetition_with_indexes \
+    "\\{\\\[0\\\] = 1, \\\[1\\\] = 3 <repeats 12 times>, \\\[13\\\] = 5, \\\[14\\\] = 5, \\\[15\\\] = 5\\}"
+
+  check_var_with_bool_opt "array_indexes" "a_point_t"
+  check_var_with_bool_opt "array_indexes" "a_point_t_pointer"
+  check_var_with_bool_opt "array_indexes" "another_point"
+  check_var_with_bool_opt "array_indexes" "a_struct_with_union"
+  check_var_with_bool_opt "array_indexes" "an_enum"
+  check_var_with_bool_opt "array_indexes" "a_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string_array"
+  check_var_with_bool_opt "array_indexes" "a_big_string"
+  check_var_with_bool_opt "array_indexes" "an_array" \
+    $an_array_with_indexes
+  check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+    $an_array_with_repetition_with_indexes
+  check_var_with_bool_opt "array_indexes" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "array_indexes" "a_point_t_ref"
+    check_var_with_bool_opt "array_indexes" "a_base_ref"
+  }
+
+  with_temp_option \
+	"set print array-indexes on" \
+	"set print array-indexes off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array" \
+      $an_array_with_indexes
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+  }
+}
+
+# Test the symbols option for gdb.Value.format_string.
+proc test_symbols {} {
+  global undefined
+  global current_lang
+  global default_pointer_regexp
+
+  check_var_with_bool_opt "symbols" "a_point_t"
+  check_var_with_bool_opt "symbols" "a_point_t_pointer"
+  check_var_with_bool_opt "symbols" "another_point"
+  check_var_with_bool_opt "symbols" "a_struct_with_union"
+  check_var_with_bool_opt "symbols" "an_enum"
+  check_var_with_bool_opt "symbols" "a_string"
+  check_var_with_bool_opt "symbols" "a_binary_string"
+  check_var_with_bool_opt "symbols" "a_binary_string_array"
+  check_var_with_bool_opt "symbols" "a_big_string"
+  check_var_with_bool_opt "symbols" "an_array"
+  check_var_with_bool_opt "symbols" "an_array_with_repetition"
+  check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+    $undefined \
+    $default_pointer_regexp
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "symbols" "a_point_t_ref"
+    check_var_with_bool_opt "symbols" "a_base_ref"
+  }
+
+  with_temp_option "set print symbol off" "set print symbol on" {
+    check_var_with_no_opts "a_symbol_pointer" \
+      $default_pointer_regexp
+    check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+      $undefined \
+      $default_pointer_regexp
+  }
+}
+
+# Test the unions option for gdb.Value.format_string.
+proc test_unions {} {
+  global undefined
+  global current_lang
+
+  check_var_with_bool_opt "unions" "a_point_t"
+  check_var_with_bool_opt "unions" "a_point_t_pointer"
+  check_var_with_bool_opt "unions" "another_point"
+  check_var_with_bool_opt "unions" "a_struct_with_union" \
+    $undefined \
+    "\\{the_union = \\{...\\}\\}"
+  check_var_with_bool_opt "unions" "an_enum"
+  check_var_with_bool_opt "unions" "a_string"
+  check_var_with_bool_opt "unions" "a_binary_string"
+  check_var_with_bool_opt "unions" "a_binary_string_array"
+  check_var_with_bool_opt "unions" "a_big_string"
+  check_var_with_bool_opt "unions" "an_array"
+  check_var_with_bool_opt "unions" "an_array_with_repetition"
+  check_var_with_bool_opt "unions" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "unions" "a_point_t_ref"
+    check_var_with_bool_opt "unions" "a_base_ref"
+  }
+
+  with_temp_option "set print union off" "set print union on" {
+    check_var_with_no_opts "a_struct_with_union" \
+      "\\{the_union = \\{...\\}\\}"
+    check_var_with_bool_opt "unions" "a_struct_with_union" \
+      $undefined \
+      "\\{the_union = \\{...\\}\\}"
+  }
+}
+
+# Test the deref_refs option for gdb.Value.format_string.
+proc test_deref_refs {} {
+  global current_lang
+  global default_pointer_regexp
+  global default_ref_regexp
+
+  check_var_with_bool_opt "deref_refs" "a_point_t"
+  check_var_with_bool_opt "deref_refs" "a_point_t_pointer"
+  check_var_with_bool_opt "deref_refs" "another_point"
+  check_var_with_bool_opt "deref_refs" "a_struct_with_union"
+  check_var_with_bool_opt "deref_refs" "an_enum"
+  check_var_with_bool_opt "deref_refs" "a_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string_array"
+  check_var_with_bool_opt "deref_refs" "a_big_string"
+  check_var_with_bool_opt "deref_refs" "an_array"
+  check_var_with_bool_opt "deref_refs" "an_array_with_repetition"
+  check_var_with_bool_opt "deref_refs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "deref_refs" "a_point_t_ref"
+    check_var_with_bool_opt "deref_refs" "a_base_ref" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42, static a_static_member = 2019\\}"
+  }
+}
+
+# Test the actual_objects option for gdb.Value.format_string.
+proc test_actual_objects {} {
+  global current_lang
+
+  check_var_with_bool_opt "actual_objects" "a_point_t"
+  check_var_with_bool_opt "actual_objects" "a_point_t_pointer"
+  check_var_with_bool_opt "actual_objects" "another_point"
+  check_var_with_bool_opt "actual_objects" "a_struct_with_union"
+  check_var_with_bool_opt "actual_objects" "an_enum"
+  check_var_with_bool_opt "actual_objects" "a_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string_array"
+  check_var_with_bool_opt "actual_objects" "a_big_string"
+  check_var_with_bool_opt "actual_objects" "an_array"
+  check_var_with_bool_opt "actual_objects" "an_array_with_repetition"
+  check_var_with_bool_opt "actual_objects" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+    check_var_with_bool_opt "actual_objects" "a_base_ref"
+
+    with_temp_option "set print object on" "set print object off" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "actual_objects" "a_base_ref"
+    }
+  }
+}
+
+# Test the static_members option for gdb.Value.format_string.
+proc test_static_members {} {
+  global current_lang
+
+  check_var_with_bool_opt "static_members" "a_point_t"
+  check_var_with_bool_opt "static_members" "a_point_t_pointer"
+  check_var_with_bool_opt "static_members" "another_point"
+  check_var_with_bool_opt "static_members" "a_struct_with_union"
+  check_var_with_bool_opt "static_members" "an_enum"
+  check_var_with_bool_opt "static_members" "a_string"
+  check_var_with_bool_opt "static_members" "a_binary_string"
+  check_var_with_bool_opt "static_members" "a_binary_string_array"
+  check_var_with_bool_opt "static_members" "a_big_string"
+  check_var_with_bool_opt "static_members" "an_array"
+  check_var_with_bool_opt "static_members" "an_array_with_repetition"
+  check_var_with_bool_opt "static_members" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "static_members" "a_point_t_ref"
+    check_var_with_bool_opt "static_members" "a_base_ref"
+
+    with_temp_option \
+	"set print static-members off" \
+	"set print static-members on" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "static_members" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "static_members" "a_base_ref"
+    }
+  }
+}
+
+# Test the max_elements option for gdb.Value.format_string.
+proc test_max_elements {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 200 is the default maximum number of elements, so setting it should
+  # not change the output.
+  set opts "max_elements=200"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "max_elements=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    # This will print four characters instead of three, see
+    # <https://sourceware.org/bugzilla/show_bug.cgi?id=24331>.
+    check_format_string "a_binary_string_array" $opts \
+      "\"hell\"..."
+    check_format_string "a_big_string" $opts \
+      [get_cut_big_string 3]
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>...\\}"
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # mean no truncation.
+  foreach opts { "max_elements=1000" "max_elements=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts \
+        [get_cut_big_string 1000]
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print elements 4" "set print elements 200" {
+    check_format_string "a_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string_array" "" \
+      "\"hell\"..."
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>...\\}"
+  }
+}
+
+# Test the repeat_threshold option for gdb.Value.format_string.
+proc test_repeat_threshold {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 10 is the default threshold for repeated items, so setting it should
+  # not change the output.
+  set opts "repeat_threshold=10"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=1"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o world\""
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o\""
+    check_format_string "a_binary_string_array" $opts \
+      "\"he\", 'l' <repeats 2 times>, \"o\\\\000world\""
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 100 (we don't have that many repeated elements) and 0 (unlimited)
+  # should mean no truncation.
+  foreach opts { "repeat_threshold=100" "repeat_threshold=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+        "\\{1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5\\}"
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print repeats 1" "set print repeats 10" {
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+  }
+}
+
+# Test the format option for gdb.Value.format_string.
+proc test_format {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # Hexadecimal.
+  set opts "format='x'"
+  with_test_prefix $opts {
+    gdb_test "python print (gdb.Value (42).format_string (${opts}))" \
+      "0x2a" \
+      "42 with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 0x2a, a_char = 0x2a\\}\\}"
+    check_format_string "an_enum" $opts \
+      "0x1"
+    check_format_string "a_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{0x41, 0x42, 0x43, 0x44, 0x45, \[, x0-9a-f\]+\.\.\.\\}"
+    check_format_string "an_array" $opts \
+      "\\{0x2, 0x3, 0x5\\}"
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{0x1, 0x3 <repeats 12 times>, 0x5, 0x5, 0x5\\}"
+    check_format_string "a_symbol_pointer" $opts \
+      $default_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Decimal.
+  set opts "format='d'"
+  with_test_prefix $opts {
+    set decimal_pointer_regexp "\[0-9\]+"
+    gdb_test "python print (gdb.Value (0x2a).format_string (${opts}))" \
+      "42" \
+      "0x2a with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts \
+      $decimal_pointer_regexp
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 42, a_char = 42\\}\\}"
+    check_format_string "an_enum" $opts \
+      "1"
+    check_format_string "a_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{104, 101, 108, 108, 111, 0, 119, 111, 114, 108, 100, 0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{65, 66, 67, 68, 69, \[, 0-9\]+\.\.\.\\}"
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts \
+      $decimal_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+}
+
+# Test mixing options.
+proc test_mixed {} {
+  global current_lang
+  global default_ref_regexp
+  global default_pointer_regexp
+
+  check_format_string "a_point_t" \
+    "raw=True, format='x'" \
+    "\\{x = 0x2a, y = 0xc\\}"
+
+  check_format_string "an_array" \
+    "array_indexes=True, pretty_arrays=True" \
+    "  \\{\\\[0\\\] = 2,\[\r\n\]+  \\\[1\\\] = 3,\[\r\n\]+  \\\[2\\\] = 5\\}"
+
+  check_format_string "a_struct_with_union" \
+    "pretty_structs=True, unions=False" \
+    "\\{\[\r\n\]+  the_union = \\{\.\.\.\\}\[\r\n\]+\\}"
+
+  check_format_string "a_symbol_pointer" \
+    "symbols=False, format='d'" \
+    "\[0-9\]+"
+
+  if { $current_lang == "c++" } {
+    check_format_string "a_point_t_ref" \
+      "deref_refs=True, actual_objects=True, raw=True" \
+      "${default_ref_regexp}: \\{x = 42, y = 12\\}"
+
+    check_format_string "a_base_ref" \
+      "deref_refs=True, static_members=False" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42\\}"
+  }
+}
+
+# Test passing invalid arguments to gdb.Value.format_string.
+proc test_invalid_args {} {
+  check_format_string \
+    "a_point_t" \
+    "12" \
+    "TypeError: format_string\\(\\) takes 0 positional arguments but 1 were given.*"
+
+  check_format_string \
+    "a_point_t" \
+    "invalid=True" \
+    "TypeError: 'invalid' is an invalid keyword argument for this function.*"
+
+  check_format_string \
+    "a_point_t" \
+    "raw='hello'" \
+    "TypeError: argument 1 must be bool, not str.*"
+
+  check_format_string \
+    "a_point_t" \
+    "format='xd'" \
+    "ValueError: a single character is required.*"
+}
+
+# Run all the tests in common for both C and C++.
+proc test_all_common {} {
+  # No options.
+  test_no_opts
+  # Single options set to True/False.
+  test_raw
+  test_pretty_arrays
+  test_pretty_structs
+  test_array_indexes
+  test_symbols
+  test_unions
+  test_deref_refs
+  test_actual_objects
+  test_static_members
+  test_max_elements
+  test_repeat_threshold
+  test_format
+  # Multiple options mixed together.
+  test_mixed
+  # Various error conditions.
+  test_invalid_args
+}
+
+# The current language ("c" or "c++" while running tests).
+set current_lang ""
+
+with_test_prefix "format_string" {
+  # Perform C Tests.
+  if { [build_inferior "${binfile}" "c"] == 0 } {
+    with_test_prefix "lang_c" {
+      set current_lang "c"
+      prepare_gdb "${binfile}"
+      test_all_common
+    }
+  }
+
+  # Perform C++ Tests.
+  if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
+    with_test_prefix "lang_cpp" {
+      set current_lang "c++"
+      prepare_gdb "${binfile}-cxx"
+      test_all_common
+    }
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/gdb.python/py-format-string.py
new file mode 100644
index 0000000000..c2ad88e862
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2008-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests python pretty
+# printers.
+
+import gdb
+
+class PointPrinter (object):
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return 'Pretty Point (%s, %s)' % (self.val['x'], self.val['y'])
+
+def test_lookup_function (val):
+    "Look-up and return a pretty-printer that can print val."
+
+    # Get the type.
+    type = val.type
+
+    # If it points to a reference, get the reference.
+    if type.code == gdb.TYPE_CODE_REF:
+        type = type.target ()
+
+    # Get the unqualified type, stripped of typedefs.
+    type = type.unqualified ().strip_typedefs ()
+
+    # Get the type name.
+    typename = type.tag
+
+    if typename == 'point':
+        return PointPrinter (val)
+
+    return None
+
+gdb.pretty_printers.append (test_lookup_function)
-- 
2.19.2


[-- Attachment #3: Type: text/plain, Size: 23 bytes --]



-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2] Add gdb.Value.format_string ()
  2019-03-18  9:22   ` [PATCH v2] " Marco Barisione
@ 2019-03-18 16:34     ` Kevin Buettner
  2019-03-18 17:49       ` Eli Zaretskii
                         ` (2 more replies)
  2019-03-18 17:49     ` [PATCH v2] " Eli Zaretskii
  1 sibling, 3 replies; 16+ messages in thread
From: Kevin Buettner @ 2019-03-18 16:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Marco Barisione, Eli Zaretskii, Tom Tromey

On Mon, 18 Mar 2019 09:22:36 +0000
Marco Barisione <mbarisione@undo.io> wrote:

> Here's my updated patch fixing Kevin's review comments.

Your updated patch looks good to me.

Eli needs to approve your additions to gdb/doc/python.texi.

I'd also like Tom to look over your proposed changes to GDB's python
interface.

I've CC'd both Tom and Eli on this reply.

Kevin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2] Add gdb.Value.format_string ()
  2019-03-18 16:34     ` Kevin Buettner
@ 2019-03-18 17:49       ` Eli Zaretskii
  2019-03-18 18:17       ` [PATCH v3] " Marco Barisione
       [not found]       ` <D8FDF6CA-2755-4AC2-BE63-A76311F566E6@undo.io>
  2 siblings, 0 replies; 16+ messages in thread
From: Eli Zaretskii @ 2019-03-18 17:49 UTC (permalink / raw)
  To: Kevin Buettner; +Cc: gdb-patches, mbarisione, tom

> Date: Mon, 18 Mar 2019 09:34:41 -0700
> From: Kevin Buettner <kevinb@redhat.com>
> Cc: Marco Barisione <mbarisione@undo.io>, Eli Zaretskii <eliz@gnu.org>, Tom Tromey <tom@tromey.com>
> 
> Eli needs to approve your additions to gdb/doc/python.texi.

Sorry, I missed that because it was in an attachment.  Reviewed now.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2] Add gdb.Value.format_string ()
  2019-03-18  9:22   ` [PATCH v2] " Marco Barisione
  2019-03-18 16:34     ` Kevin Buettner
@ 2019-03-18 17:49     ` Eli Zaretskii
  1 sibling, 0 replies; 16+ messages in thread
From: Eli Zaretskii @ 2019-03-18 17:49 UTC (permalink / raw)
  To: Marco Barisione; +Cc: gdb-patches, kevinb

> From: Marco Barisione <mbarisione@undo.io>
> Date: Mon, 18 Mar 2019 09:22:36 +0000
> Cc: Kevin Buettner <kevinb@redhat.com>
> 
> gdb/doc/ChangeLog:
> 
> 2019-03-18  Marco Barisione  <mbarisione@undo.io>
> 
> 	* python.texi: Document gdb.Value.format_string ().

Please state the node name where you made the changes, as if it were a
function (i.e. in parentheses).

> diff --git a/gdb/NEWS b/gdb/NEWS
> index c45b313406..e4d9ae0fea 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -231,6 +231,12 @@ before Windows XP.
>    ** The gdb.Value type has a new constructor, which is used to construct a
>       gdb.Value from a Python buffer object and a gdb.Type.
>  
> +  ** The gdb.Value type has a new method 'format_string' which returns a
> +     string representing the value.  The formatting is controlled by the
> +     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
> +     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
> +     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
> +

This part is OK.

> +@code{True} if C++ references should be resolved to the value they refer
                  ^^^
I believe we prefer to use C@t{++}, as it looks better in print.

> +to, @code{False} (the default) if they shouldn't.  Note that, unlike for
> +the @code{print} command, references are not automatically expanded when
> +using the @code{format_string ()} method or the @code{str ()} function.

Please don't use "foo ()" to indicate that 'foo' is a function: the
result looks like a call to 'foo' with no arguments, which is not what
you mean Just @code{format_string} is enough, and likewise for 'str'.

> +representation of a C++ object, @code{False} if they shouldn't (see
                       ^^^
C@t{++}

OK with those nits fixed.

Thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3] Add gdb.Value.format_string ()
  2019-03-18 16:34     ` Kevin Buettner
  2019-03-18 17:49       ` Eli Zaretskii
@ 2019-03-18 18:17       ` Marco Barisione
  2019-03-27 20:16         ` Tom Tromey
       [not found]       ` <D8FDF6CA-2755-4AC2-BE63-A76311F566E6@undo.io>
  2 siblings, 1 reply; 16+ messages in thread
From: Marco Barisione @ 2019-03-18 18:17 UTC (permalink / raw)
  To: Kevin Buettner; +Cc: gdb-patches, Eli Zaretskii, Tom Tromey

[-- Attachment #1: Type: text/plain, Size: 396 bytes --]

On 18 Mar 2019, at 16:34, Kevin Buettner <kevinb@redhat.com> wrote:
> Your updated patch looks good to me.

Thanks!

> Eli needs to approve your additions to gdb/doc/python.texi.

Fixed Eli’s review concerns in the attached patch.

> I'd also like Tom to look over your proposed changes to GDB's python
> interface.
> 
> I've CC'd both Tom and Eli on this reply.

Thank you.



[-- Attachment #2: 0001-Add-gdb.Value.format_string.patch --]
[-- Type: application/octet-stream, Size: 53062 bytes --]

From f61fdff0c65acaf6097b6d4d6db6dcd22a758e0b Mon Sep 17 00:00:00 2001
From: Marco Barisione <mbarisione@undo.io>
Date: Mon, 18 Mar 2019 17:59:03 +0000
Subject: [PATCH] Add gdb.Value.format_string ()

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

This patch introduces a new format_string () method to gdb.Value which
allows specifying more formatting options, thus giving access to more
features provided by the internal C function common_val_print ().

gdb/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Add gdb.Value.format_string ().
	* python/py-value.c (copy_py_bool_obj):
	(valpy_format_string): Add gdb.Value.format_string ().
	* NEWS: Document the addition of gdb.Value.format_string ().

gdb/doc/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	* python.texi (Values From Inferior): Document
	gdb.Value.format_string ().

gdb/testsuite/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Test gdb.Value.format_string ().
	* gdb.python/py-format-string.exp: New test.
	* gdb.python/py-format-string.c: New file.
	* gdb.python/py-format-string.py: New file.
---
 gdb/NEWS                                      |   6 +
 gdb/doc/python.texi                           |  80 ++
 gdb/python/py-value.c                         | 162 +++
 gdb/testsuite/gdb.python/py-format-string.c   | 118 +++
 gdb/testsuite/gdb.python/py-format-string.exp | 958 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-format-string.py  |  49 +
 6 files changed, 1373 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.c
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.exp
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.py

diff --git a/gdb/NEWS b/gdb/NEWS
index c45b313406..e4d9ae0fea 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -231,6 +231,12 @@ before Windows XP.
   ** The gdb.Value type has a new constructor, which is used to construct a
      gdb.Value from a Python buffer object and a gdb.Type.
 
+  ** The gdb.Value type has a new method 'format_string' which returns a
+     string representing the value.  The formatting is controlled by the
+     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
+     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
+     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
+
 * Configure changes
 
 --enable-ubsan
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index b02a154ce1..1d89be9e9d 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -864,6 +864,86 @@ Like @code{Value.cast}, but works as if the C@t{++} @code{reinterpret_cast}
 operator were used.  Consult a C@t{++} reference for details.
 @end defun
 
+@defun Value.format_string (...)
+Convert a @code{gdb.Value} to a string, similarly to what the @code{print}
+command does.  Invoked with no arguments, this is equivalent to calling
+the @code{str} function on the @code{gdb.Value}.  The representation of
+the same value may change across different versions of @value{GDBN}, so
+you shouldn't, for instance, parse the strings returned by this method.
+
+All the arguments are keyword only.  If an argument is not specified, the
+current global default setting is used.
+
+@table @code
+@item raw
+@code{True} if pretty-printers (@pxref{Pretty Printing}) should not be
+used to format the value.  @code{False} if enabled pretty-printers
+matching the type represented by the @code{gdb.Value} should be used to
+format it.
+
+@item pretty_arrays
+@code{True} if arrays should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print array} in
+@ref{Print Settings}).
+
+@item pretty_structs
+@code{True} if structs should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print pretty} in
+@ref{Print Settings}).
+
+@item array_indexes
+@code{True} if array indexes should be included in the string
+representation of arrays, @code{False} if they shouldn't (see @code{set
+print array-indexes} in @ref{Print Settings}).
+
+@item symbols
+@code{True} if the string representation of a pointer should include the
+corresponding symbol name (if one exists), @code{False} if it shouldn't
+(see @code{set print symbol} in @ref{Print Settings}).
+
+@item unions
+@code{True} if unions which are contained in other structures or unions
+should be expanded, @code{False} if they shouldn't (see @code{set print
+union} in @ref{Print Settings}).
+
+@item deref_refs
+@code{True} if C@t{++} references should be resolved to the value they
+refer to, @code{False} (the default) if they shouldn't.  Note that, unlike
+for the @code{print} command, references are not automatically expanded
+when using the @code{format_string()} method or the @code{str()}
+function.  There is no global @code{print} setting to change the default
+behaviour.
+
+@item actual_objects
+@code{True} if the representation of a pointer to an object should
+identify the @emph{actual} (derived) type of the object rather than the
+@emph{declared} type, using the virtual function table.  @code{False} if
+the @emph{declared} type should be used.  (See @code{set print object} in
+@ref{Print Settings}).
+
+@item static_fields
+@code{True} if static members should be included in the string
+representation of a C@t{++} object, @code{False} if they shouldn't (see
+@code{set print static-members} in @ref{Print Settings}).
+
+@item max_elements
+Number of array elements to print, or @code{0} to print an unlimited
+number of elements (see @code{set print elements} in @ref{Print
+Settings}).
+
+@item repeat_threshold
+Set the threshold for suppressing display of repeated array elements, or
+@code{0} to represent all elements, even if repeated.  (See @code{set
+print repeats} in @ref{Print Settings}).
+
+@item format
+A string containing a single character representing the format to use for
+the returned string.  For instance, @code{'x'} is equivalent to using the
+@value{GDBN} command @code{print} with the @code{/x} option and formats
+the value as a hexadecimal number.
+@end table
+@end defun
+
 @defun Value.string (@r{[}encoding@r{[}, errors@r{[}, length@r{]]]})
 If this @code{gdb.Value} represents a string, then this method
 converts the contents to a Python string.  Otherwise, this method will
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index dd6a536b6a..17f85d97d5 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -588,6 +588,163 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
 			   encoding, errors);
 }
 
+/* Given a Python object, copy its truth value to a C int (the value
+   pointed by dest).
+   If src_obj is NULL, then *dest is not modified.
+
+   Return true in case of success (including src_obj being NULL), false
+   in case of error.  */
+
+static bool
+copy_py_bool_obj (int *dest, PyObject *src_obj)
+{
+  if (src_obj)
+    {
+      int cmp = PyObject_IsTrue (src_obj);
+      if (cmp < 0)
+	return false;
+      *dest = cmp;
+    }
+
+  return true;
+}
+
+/* Implementation of gdb.Value.format_string (...) -> string.
+   Return Unicode string with value contents formatted using the
+   keyword-only arguments.  */
+
+static PyObject *
+valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = {
+      /* Basic C/C++ options.  */
+      "raw",			/* See the /r option to print.  */
+      "pretty_arrays",		/* See set print array on|off.  */
+      "pretty_structs",		/* See set print pretty on|off.  */
+      "array_indexes",		/* See set print array-indexes on|off.  */
+      "symbols",		/* See set print symbol on|off.  */
+      "unions",			/* See set print union on|off.  */
+      /* C++ options.  */
+      "deref_refs",		/* No corresponding setting.  */
+      "actual_objects",		/* See set print object on|off.  */
+      "static_members",		/* See set print static-members on|off.  */
+      /* C non-bool options.  */
+      "max_elements", 		/* See set print elements N.  */
+      "repeat_threshold",	/* See set print repeats.  */
+      "format",			/* The format passed to the print command.  */
+      NULL };
+
+  /* This function has too many arguments to be useful as positionals, so
+     the user should specify them all as keyword arguments.
+     Python 3.3 and later have a way to specify it (both in C and Python
+     itself), but we could be compiled with older versions, so we just
+     check that the args tuple is empty.  */
+  Py_ssize_t positional_count = PyObject_Length (args);
+  if (positional_count < 0)
+    return NULL;
+  else if (positional_count > 0)
+    {
+      /* This matches the error message that Python 3.3 raises when
+	 passing positionals to functions expecting keyword-only
+	 arguments.  */
+      PyErr_Format (PyExc_TypeError,
+		    "format_string() takes 0 positional arguments but %zu were given",
+		    positional_count);
+      return NULL;
+    }
+
+  struct value_print_options opts;
+  get_user_print_options (&opts);
+  opts.deref_ref = 0;
+
+  /* We need objects for booleans as the "p" flag for bools is new in
+     Python 3.3.  */
+  PyObject *raw_obj = NULL;
+  PyObject *pretty_arrays_obj = NULL;
+  PyObject *pretty_structs_obj = NULL;
+  PyObject *array_indexes_obj = NULL;
+  PyObject *symbols_obj = NULL;
+  PyObject *unions_obj = NULL;
+  PyObject *deref_refs_obj = NULL;
+  PyObject *actual_objects_obj = NULL;
+  PyObject *static_members_obj = NULL;
+  char *format = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (args,
+					kw,
+					"|O!O!O!O!O!O!O!O!O!IIs",
+					keywords,
+					&PyBool_Type, &raw_obj,
+					&PyBool_Type, &pretty_arrays_obj,
+					&PyBool_Type, &pretty_structs_obj,
+					&PyBool_Type, &array_indexes_obj,
+					&PyBool_Type, &symbols_obj,
+					&PyBool_Type, &unions_obj,
+					&PyBool_Type, &deref_refs_obj,
+					&PyBool_Type, &actual_objects_obj,
+					&PyBool_Type, &static_members_obj,
+					&opts.print_max,
+					&opts.repeat_count_threshold,
+					&format))
+    return NULL;
+
+  /* Set boolean arguments.  */
+  if (!copy_py_bool_obj (&opts.raw, raw_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_arrays, pretty_arrays_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_structs, pretty_structs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.print_array_indexes, array_indexes_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.symbol_print, symbols_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.unionprint, unions_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.deref_ref, deref_refs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.objectprint, actual_objects_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.static_field_print, static_members_obj))
+    return NULL;
+
+  /* Numeric arguments for which 0 means unlimited (which we represent as
+     UINT_MAX).  */
+  if (opts.print_max == 0)
+    opts.print_max = UINT_MAX;
+  if (opts.repeat_count_threshold == 0)
+    opts.repeat_count_threshold = UINT_MAX;
+
+  /* Other arguments.  */
+  if (format != NULL)
+    {
+      if (strlen (format) == 1)
+	opts.format = format[0];
+      else
+	{
+	  /* Mimic the message on standard Python ones for similar
+	     errors.  */
+	  PyErr_SetString (PyExc_ValueError,
+			   "a single character is required");
+	  return NULL;
+	}
+    }
+
+  string_file stb;
+
+  TRY
+    {
+      common_val_print (((value_object *) self)->value, &stb, 0,
+			&opts, python_language);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return PyUnicode_Decode (stb.c_str (), stb.size (), host_charset (), NULL);
+}
+
 /* A helper function that implements the various cast operators.  */
 
 static PyObject *
@@ -1944,6 +2101,11 @@ Return a lazy string representation of the value." },
 Return Unicode string representation of the value." },
   { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
     "Fetches the value from the inferior, if it was lazy." },
+  { "format_string", (PyCFunction) valpy_format_string,
+    METH_VARARGS | METH_KEYWORDS,
+    "format_string (...) -> string\n\
+Return a string representation of the value using the specified\n\
+formatting options" },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-format-string.c b/gdb/testsuite/gdb.python/py-format-string.c
new file mode 100644
index 0000000000..120ecce989
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.c
@@ -0,0 +1,118 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+typedef struct point
+{
+  int x;
+  int y;
+} point_t;
+
+typedef union
+{
+  int an_int;
+  char a_char;
+} union_t;
+
+typedef struct
+{
+  union_t the_union;
+} struct_union_t;
+
+typedef enum
+{
+  ENUM_FOO,
+  ENUM_BAR,
+} enum_t;
+
+typedef void (*function_t) (int);
+
+static void
+my_function(int n)
+{
+}
+
+#ifdef __cplusplus
+
+struct Base
+{
+  Base (int a_) : a (a_) {}
+
+  virtual int get_number () { return a; }
+
+  int a;
+
+  static int a_static_member;
+};
+
+int Base::a_static_member = 2019;
+
+struct Deriv : Base
+{
+  Deriv (int b_) : Base (42), b (b_) {}
+
+  virtual int get_number () { return b; }
+
+  int b;
+};
+
+#endif
+
+int global_symbol = 42;
+
+int
+main ()
+{
+  point_t a_point_t = { 42, 12 };
+  point_t *a_point_t_pointer = &a_point_t;
+#ifdef __cplusplus
+  point_t &a_point_t_ref = a_point_t;
+#endif
+  struct point another_point = { 123, 456 };
+
+  struct_union_t a_struct_with_union;
+  a_struct_with_union.the_union.an_int = 42;
+
+  enum_t an_enum = ENUM_BAR;
+
+  const char *a_string = "hello world";
+  const char *a_binary_string = "hello\0world";
+  const char a_binary_string_array[] = "hello\0world";
+
+  const int letters_repeat = 10;
+  char a_big_string[26 * letters_repeat + 1];
+  a_big_string[26 * letters_repeat] = '\0';
+  for (int i = 0; i < letters_repeat; i++)
+    for (char c = 'A'; c <= 'Z'; c++)
+      a_big_string[i * 26 + c - 'A'] = c;
+
+  int an_array[] = { 2, 3, 5 };
+
+  int an_array_with_repetition[] = {
+    1,					/*  1 time.   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 12 times.  */
+    5, 5, 5,				/*  3 times   */
+    };
+
+  int *a_symbol_pointer = &global_symbol;
+
+#ifdef __cplusplus
+  Deriv a_deriv (123);
+  Base &a_base_ref = a_deriv;
+#endif
+
+  return 0; /* break here */
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
new file mode 100644
index 0000000000..4dbe3f80fa
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -0,0 +1,958 @@
+# Copyright (C) 2009-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Value.format_string () method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [get_compiler_info c++] {
+    return -1
+}
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc prepare_gdb {exefile} {
+  global srcdir subdir srcfile testfile hex
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+  gdb_load ${exefile}
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  if ![runto_main] then {
+      perror "couldn't run to breakpoint"
+      return
+  }
+
+  # Load the pretty printer.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+  runto_bp "break here"
+}
+
+# Set breakpoint and run to that breakpoint.
+proc runto_bp {bp} {
+  gdb_breakpoint [gdb_get_line_number $bp]
+  gdb_continue_to_breakpoint $bp
+}
+
+# Set an option using the GDB command in $set_cmd, execute $body, and then
+# restore the option using the GDB command in $unset_cmd.
+proc with_temp_option { set_cmd unset_cmd body } {
+  with_test_prefix $set_cmd {
+    gdb_test "$set_cmd" ".*"
+    uplevel 1 $body
+    gdb_test "$unset_cmd" ".*"
+  }
+}
+
+# A regular expression for a pointer.
+set default_pointer_regexp "0x\[a-fA-F0-9\]+"
+
+# A regular expression for a non-expanded C++ reference.
+#
+# Stringifying a C++ reference produces an address preceeded by a "@" in
+# Python, but, by default, the C++ reference/class is expanded by the
+# GDB print command.
+set default_ref_regexp "@${default_pointer_regexp}"
+
+# The whole content of the C variable a_big_string, i.e. the whole English
+# alphabet repeated 10 times.
+set whole_big_string ""
+set alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+for {set i 0} {$i < 10} {incr i} {
+  append whole_big_string $alphabet
+}
+unset alphabet
+
+# Produces a potentially cut down version of $whole_big_string like GDB
+# would represent it.
+# $max is the maximum number of characters allowed in the string (but
+# the return value may contain more to accound for the extra quotes and
+# "..." added by GDB).
+proc get_cut_big_string { max } {
+  global whole_big_string
+
+  set whole_size [string length $whole_big_string]
+  if { $max > $whole_size } {
+    return "\"${whole_big_string}\""
+  }
+
+  set cut_string [string range $whole_big_string 0 [expr $max - 1]]
+  return "\"${cut_string}\"..."
+}
+
+# A dictionary mapping from C variable names to their default string
+# representation when using str () or gdb.Value.format_string () with
+# no arguments.
+# This usually matches what the print command prints if used with no
+# options, except for C++ references which are not expanded by
+# default in Python.  See the comment above $default_ref_regexp.
+set default_regexp_dict [dict create \
+  "a_point_t"			"Pretty Point \\(42, 12\\)" \
+  "a_point_t_pointer"		$default_pointer_regexp \
+  "a_point_t_ref"		"Pretty Point \\(42, 12\\)" \
+  "another_point"		"Pretty Point \\(123, 456\\)" \
+  "a_struct_with_union"		"\\{the_union = \\{an_int = 42, a_char = 42 '\\*'\\}\\}" \
+  "an_enum"			"ENUM_BAR" \
+  "a_string"			"${default_pointer_regexp} \"hello world\"" \
+  "a_binary_string"		"${default_pointer_regexp} \"hello\"" \
+  "a_binary_string_array"	"\"hello\\\\000world\"" \
+  "a_big_string"		[get_cut_big_string 200] \
+  "an_array"			"\\{2, 3, 5\\}" \
+  "an_array_with_repetition"	"\\{1, 3 <repeats 12 times>, 5, 5, 5\\}" \
+  "a_symbol_pointer"		"${default_pointer_regexp} <global_symbol>" \
+  "a_base_ref"			"${default_ref_regexp}" \
+  ]
+
+# A sentinel value to pass to function to get them to use a default value
+# instead.
+# Note that we cannot use $undefined for default arguments in function
+# definitions as we would just get the literal "$undefined" string, so
+# we need to repeat the string.
+set undefined "\000UNDEFINED\000"
+
+# Return $value if it's not $undefined, otherwise return the default value
+# (from $default_regexp_dict) for the variable $var.
+proc get_value_or_default { var value } {
+  global undefined
+  if { $value != $undefined } {
+    return $value
+  }
+
+  global default_regexp_dict
+  return [dict get $default_regexp_dict $var]
+}
+
+# Check that using gdb.Value.format_string on the value representing the
+# variable $var produces $expected.
+proc check_format_string {
+	var
+	opts
+	{ expected "\000UNDEFINED\000" }
+	{ name "\000UNDEFINED\000" }
+  } {
+  global undefined
+
+  set expected [get_value_or_default $var $expected]
+  if { $name == $undefined } {
+    set name "${var} with option ${opts}"
+  }
+
+  gdb_test \
+    "python print (gdb.parse_and_eval ('${var}').format_string (${opts}))" \
+    $expected \
+    $name
+}
+
+# Check that printing $var with no options set, produces the expected
+# output.
+proc check_var_with_no_opts {
+	var
+	{ expected "\000UNDEFINED\000" }
+  } {
+  set expected [get_value_or_default $var $expected]
+
+  with_test_prefix "${var}" {
+    check_format_string \
+      $var \
+      "" \
+      $expected \
+      "no opts"
+    # str () should behave like gdb.Value.format_string () with no args.
+    gdb_test \
+      "python print (str (gdb.parse_and_eval ('${var}')))" \
+      $expected \
+      "str"
+  }
+}
+
+# Check that printing $var with $opt set to True and set to False,
+# produces the expected output.
+proc check_var_with_bool_opt {
+	opt
+	var
+	{ true_expected  "\000UNDEFINED\000" }
+	{ false_expected "\000UNDEFINED\000" }
+  } {
+  set true_expected  [get_value_or_default $var $true_expected]
+  set false_expected [get_value_or_default $var $false_expected]
+
+  with_test_prefix "${var} with option ${opt}" {
+    # Option set to True.
+    check_format_string \
+      $var \
+      "${opt}=True" \
+      $true_expected \
+      "${opt}=true"
+    # Option set to False.
+    check_format_string \
+      $var \
+      "${opt}=False" \
+      $false_expected \
+      "${opt}=false"
+  }
+}
+
+# Test gdb.Value.format_string with no options.
+proc test_no_opts {} {
+  global current_lang
+
+  check_var_with_no_opts "a_point_t"
+  check_var_with_no_opts "a_point_t_pointer"
+  check_var_with_no_opts "another_point"
+  check_var_with_no_opts "a_struct_with_union"
+  check_var_with_no_opts "an_enum"
+  check_var_with_no_opts "a_string"
+  check_var_with_no_opts "a_binary_string"
+  check_var_with_no_opts "a_binary_string_array"
+  check_var_with_no_opts "a_big_string"
+  check_var_with_no_opts "an_array"
+  check_var_with_no_opts "an_array_with_repetition"
+  check_var_with_no_opts "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_no_opts "a_point_t_ref"
+    check_var_with_no_opts "a_base_ref"
+  }
+}
+
+# Test the raw option for gdb.Value.format_string.
+proc test_raw {} {
+  global current_lang
+  global default_ref_regexp
+
+  check_var_with_bool_opt "raw" "a_point_t" \
+    "{x = 42, y = 12}"
+  check_var_with_bool_opt "raw" "a_point_t_pointer"
+  check_var_with_bool_opt "raw" "another_point" \
+    "{x = 123, y = 456}"
+  check_var_with_bool_opt "raw" "a_struct_with_union"
+  check_var_with_bool_opt "raw" "an_enum"
+  check_var_with_bool_opt "raw" "a_string"
+  check_var_with_bool_opt "raw" "a_binary_string"
+  check_var_with_bool_opt "raw" "a_binary_string_array"
+  check_var_with_bool_opt "raw" "a_big_string"
+  check_var_with_bool_opt "raw" "an_array"
+  check_var_with_bool_opt "raw" "an_array_with_repetition"
+  check_var_with_bool_opt "raw" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "raw" "a_point_t_ref" \
+      ${default_ref_regexp}
+    check_var_with_bool_opt "raw" "a_base_ref"
+  }
+
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "raw" "a_point_t" \
+      "{x = 42, y = 12}" \
+      "{x = 42, y = 12}"
+  }
+}
+
+# Test the pretty_arrays option for gdb.Value.format_string.
+proc test_pretty_arrays {} {
+  global current_lang
+
+  set an_array_pretty "  \\{2,\[\r\n\]+  3,\[\r\n\]+  5\\}"
+  set an_array_with_repetition_pretty \
+    "  \\{1,\[\r\n\]+  3 <repeats 12 times>,\[\r\n\]+  5,\[\r\n\]+  5,\[\r\n\]+  5\\}"
+
+  check_var_with_bool_opt "pretty_arrays" "a_point_t"
+  check_var_with_bool_opt "pretty_arrays" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_arrays" "another_point"
+  check_var_with_bool_opt "pretty_arrays" "a_struct_with_union"
+  check_var_with_bool_opt "pretty_arrays" "an_enum"
+  check_var_with_bool_opt "pretty_arrays" "a_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_arrays" "a_big_string"
+  check_var_with_bool_opt "pretty_arrays" "an_array" \
+    $an_array_pretty
+  check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+    $an_array_with_repetition_pretty
+  check_var_with_bool_opt "pretty_arrays" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_arrays" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_arrays" "a_base_ref"
+  }
+
+  with_temp_option "set print array on" "set print array off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array" \
+      $an_array_pretty
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+  }
+}
+
+# Test the pretty_structs option for gdb.Value.format_string.
+proc test_pretty_structs {} {
+  global current_lang
+
+  set a_struct_with_union_pretty \
+    "\\{\[\r\n\]+  the_union = \\{\[\r\n\]+    an_int = 42, \[\r\n\]+    a_char = 42 '\\*'\[\r\n\]+  \\}\[\r\n\]+\\}"
+  # Note the space after the colon!                        ^ here
+
+  check_var_with_bool_opt "pretty_structs" "a_point_t"
+  check_var_with_bool_opt "pretty_structs" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_structs" "another_point"
+  check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+    $a_struct_with_union_pretty
+  check_var_with_bool_opt "pretty_structs" "an_enum"
+  check_var_with_bool_opt "pretty_structs" "a_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_structs" "a_big_string"
+  check_var_with_bool_opt "pretty_structs" "an_array"
+  check_var_with_bool_opt "pretty_structs" "an_array_with_repetition"
+  check_var_with_bool_opt "pretty_structs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_structs" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_structs" "a_base_ref"
+  }
+
+  with_temp_option "set print structs on" "set print structs off" {
+    check_var_with_no_opts "a_struct_with_union"
+    check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+      $a_struct_with_union_pretty
+  }
+
+  # point_t is usually printed through the pretty printer.
+  # Try disabling it.
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "pretty_structs" "a_point_t" \
+      "\\{\[\r\n\]+  x = 42, *\[\r\n\]+  y = 12\[\r\n\]+\\}" \
+      "{x = 42, y = 12}" \
+  }
+}
+
+# Test the array_indexes option for gdb.Value.format_string.
+proc test_array_indexes {} {
+  global current_lang
+
+  set an_array_with_indexes "\\{\\\[0\\\] = 2, \\\[1\\\] = 3, \\\[2\\\] = 5\\}"
+  set an_array_with_repetition_with_indexes \
+    "\\{\\\[0\\\] = 1, \\\[1\\\] = 3 <repeats 12 times>, \\\[13\\\] = 5, \\\[14\\\] = 5, \\\[15\\\] = 5\\}"
+
+  check_var_with_bool_opt "array_indexes" "a_point_t"
+  check_var_with_bool_opt "array_indexes" "a_point_t_pointer"
+  check_var_with_bool_opt "array_indexes" "another_point"
+  check_var_with_bool_opt "array_indexes" "a_struct_with_union"
+  check_var_with_bool_opt "array_indexes" "an_enum"
+  check_var_with_bool_opt "array_indexes" "a_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string_array"
+  check_var_with_bool_opt "array_indexes" "a_big_string"
+  check_var_with_bool_opt "array_indexes" "an_array" \
+    $an_array_with_indexes
+  check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+    $an_array_with_repetition_with_indexes
+  check_var_with_bool_opt "array_indexes" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "array_indexes" "a_point_t_ref"
+    check_var_with_bool_opt "array_indexes" "a_base_ref"
+  }
+
+  with_temp_option \
+	"set print array-indexes on" \
+	"set print array-indexes off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array" \
+      $an_array_with_indexes
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+  }
+}
+
+# Test the symbols option for gdb.Value.format_string.
+proc test_symbols {} {
+  global undefined
+  global current_lang
+  global default_pointer_regexp
+
+  check_var_with_bool_opt "symbols" "a_point_t"
+  check_var_with_bool_opt "symbols" "a_point_t_pointer"
+  check_var_with_bool_opt "symbols" "another_point"
+  check_var_with_bool_opt "symbols" "a_struct_with_union"
+  check_var_with_bool_opt "symbols" "an_enum"
+  check_var_with_bool_opt "symbols" "a_string"
+  check_var_with_bool_opt "symbols" "a_binary_string"
+  check_var_with_bool_opt "symbols" "a_binary_string_array"
+  check_var_with_bool_opt "symbols" "a_big_string"
+  check_var_with_bool_opt "symbols" "an_array"
+  check_var_with_bool_opt "symbols" "an_array_with_repetition"
+  check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+    $undefined \
+    $default_pointer_regexp
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "symbols" "a_point_t_ref"
+    check_var_with_bool_opt "symbols" "a_base_ref"
+  }
+
+  with_temp_option "set print symbol off" "set print symbol on" {
+    check_var_with_no_opts "a_symbol_pointer" \
+      $default_pointer_regexp
+    check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+      $undefined \
+      $default_pointer_regexp
+  }
+}
+
+# Test the unions option for gdb.Value.format_string.
+proc test_unions {} {
+  global undefined
+  global current_lang
+
+  check_var_with_bool_opt "unions" "a_point_t"
+  check_var_with_bool_opt "unions" "a_point_t_pointer"
+  check_var_with_bool_opt "unions" "another_point"
+  check_var_with_bool_opt "unions" "a_struct_with_union" \
+    $undefined \
+    "\\{the_union = \\{...\\}\\}"
+  check_var_with_bool_opt "unions" "an_enum"
+  check_var_with_bool_opt "unions" "a_string"
+  check_var_with_bool_opt "unions" "a_binary_string"
+  check_var_with_bool_opt "unions" "a_binary_string_array"
+  check_var_with_bool_opt "unions" "a_big_string"
+  check_var_with_bool_opt "unions" "an_array"
+  check_var_with_bool_opt "unions" "an_array_with_repetition"
+  check_var_with_bool_opt "unions" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "unions" "a_point_t_ref"
+    check_var_with_bool_opt "unions" "a_base_ref"
+  }
+
+  with_temp_option "set print union off" "set print union on" {
+    check_var_with_no_opts "a_struct_with_union" \
+      "\\{the_union = \\{...\\}\\}"
+    check_var_with_bool_opt "unions" "a_struct_with_union" \
+      $undefined \
+      "\\{the_union = \\{...\\}\\}"
+  }
+}
+
+# Test the deref_refs option for gdb.Value.format_string.
+proc test_deref_refs {} {
+  global current_lang
+  global default_pointer_regexp
+  global default_ref_regexp
+
+  check_var_with_bool_opt "deref_refs" "a_point_t"
+  check_var_with_bool_opt "deref_refs" "a_point_t_pointer"
+  check_var_with_bool_opt "deref_refs" "another_point"
+  check_var_with_bool_opt "deref_refs" "a_struct_with_union"
+  check_var_with_bool_opt "deref_refs" "an_enum"
+  check_var_with_bool_opt "deref_refs" "a_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string_array"
+  check_var_with_bool_opt "deref_refs" "a_big_string"
+  check_var_with_bool_opt "deref_refs" "an_array"
+  check_var_with_bool_opt "deref_refs" "an_array_with_repetition"
+  check_var_with_bool_opt "deref_refs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "deref_refs" "a_point_t_ref"
+    check_var_with_bool_opt "deref_refs" "a_base_ref" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42, static a_static_member = 2019\\}"
+  }
+}
+
+# Test the actual_objects option for gdb.Value.format_string.
+proc test_actual_objects {} {
+  global current_lang
+
+  check_var_with_bool_opt "actual_objects" "a_point_t"
+  check_var_with_bool_opt "actual_objects" "a_point_t_pointer"
+  check_var_with_bool_opt "actual_objects" "another_point"
+  check_var_with_bool_opt "actual_objects" "a_struct_with_union"
+  check_var_with_bool_opt "actual_objects" "an_enum"
+  check_var_with_bool_opt "actual_objects" "a_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string_array"
+  check_var_with_bool_opt "actual_objects" "a_big_string"
+  check_var_with_bool_opt "actual_objects" "an_array"
+  check_var_with_bool_opt "actual_objects" "an_array_with_repetition"
+  check_var_with_bool_opt "actual_objects" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+    check_var_with_bool_opt "actual_objects" "a_base_ref"
+
+    with_temp_option "set print object on" "set print object off" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "actual_objects" "a_base_ref"
+    }
+  }
+}
+
+# Test the static_members option for gdb.Value.format_string.
+proc test_static_members {} {
+  global current_lang
+
+  check_var_with_bool_opt "static_members" "a_point_t"
+  check_var_with_bool_opt "static_members" "a_point_t_pointer"
+  check_var_with_bool_opt "static_members" "another_point"
+  check_var_with_bool_opt "static_members" "a_struct_with_union"
+  check_var_with_bool_opt "static_members" "an_enum"
+  check_var_with_bool_opt "static_members" "a_string"
+  check_var_with_bool_opt "static_members" "a_binary_string"
+  check_var_with_bool_opt "static_members" "a_binary_string_array"
+  check_var_with_bool_opt "static_members" "a_big_string"
+  check_var_with_bool_opt "static_members" "an_array"
+  check_var_with_bool_opt "static_members" "an_array_with_repetition"
+  check_var_with_bool_opt "static_members" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "static_members" "a_point_t_ref"
+    check_var_with_bool_opt "static_members" "a_base_ref"
+
+    with_temp_option \
+	"set print static-members off" \
+	"set print static-members on" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "static_members" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "static_members" "a_base_ref"
+    }
+  }
+}
+
+# Test the max_elements option for gdb.Value.format_string.
+proc test_max_elements {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 200 is the default maximum number of elements, so setting it should
+  # not change the output.
+  set opts "max_elements=200"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "max_elements=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    # This will print four characters instead of three, see
+    # <https://sourceware.org/bugzilla/show_bug.cgi?id=24331>.
+    check_format_string "a_binary_string_array" $opts \
+      "\"hell\"..."
+    check_format_string "a_big_string" $opts \
+      [get_cut_big_string 3]
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>...\\}"
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # mean no truncation.
+  foreach opts { "max_elements=1000" "max_elements=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts \
+        [get_cut_big_string 1000]
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print elements 4" "set print elements 200" {
+    check_format_string "a_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string_array" "" \
+      "\"hell\"..."
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>...\\}"
+  }
+}
+
+# Test the repeat_threshold option for gdb.Value.format_string.
+proc test_repeat_threshold {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 10 is the default threshold for repeated items, so setting it should
+  # not change the output.
+  set opts "repeat_threshold=10"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=1"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o world\""
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o\""
+    check_format_string "a_binary_string_array" $opts \
+      "\"he\", 'l' <repeats 2 times>, \"o\\\\000world\""
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 100 (we don't have that many repeated elements) and 0 (unlimited)
+  # should mean no truncation.
+  foreach opts { "repeat_threshold=100" "repeat_threshold=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+        "\\{1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5\\}"
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print repeats 1" "set print repeats 10" {
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+  }
+}
+
+# Test the format option for gdb.Value.format_string.
+proc test_format {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # Hexadecimal.
+  set opts "format='x'"
+  with_test_prefix $opts {
+    gdb_test "python print (gdb.Value (42).format_string (${opts}))" \
+      "0x2a" \
+      "42 with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 0x2a, a_char = 0x2a\\}\\}"
+    check_format_string "an_enum" $opts \
+      "0x1"
+    check_format_string "a_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{0x41, 0x42, 0x43, 0x44, 0x45, \[, x0-9a-f\]+\.\.\.\\}"
+    check_format_string "an_array" $opts \
+      "\\{0x2, 0x3, 0x5\\}"
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{0x1, 0x3 <repeats 12 times>, 0x5, 0x5, 0x5\\}"
+    check_format_string "a_symbol_pointer" $opts \
+      $default_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Decimal.
+  set opts "format='d'"
+  with_test_prefix $opts {
+    set decimal_pointer_regexp "\[0-9\]+"
+    gdb_test "python print (gdb.Value (0x2a).format_string (${opts}))" \
+      "42" \
+      "0x2a with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts \
+      $decimal_pointer_regexp
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 42, a_char = 42\\}\\}"
+    check_format_string "an_enum" $opts \
+      "1"
+    check_format_string "a_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{104, 101, 108, 108, 111, 0, 119, 111, 114, 108, 100, 0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{65, 66, 67, 68, 69, \[, 0-9\]+\.\.\.\\}"
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts \
+      $decimal_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+}
+
+# Test mixing options.
+proc test_mixed {} {
+  global current_lang
+  global default_ref_regexp
+  global default_pointer_regexp
+
+  check_format_string "a_point_t" \
+    "raw=True, format='x'" \
+    "\\{x = 0x2a, y = 0xc\\}"
+
+  check_format_string "an_array" \
+    "array_indexes=True, pretty_arrays=True" \
+    "  \\{\\\[0\\\] = 2,\[\r\n\]+  \\\[1\\\] = 3,\[\r\n\]+  \\\[2\\\] = 5\\}"
+
+  check_format_string "a_struct_with_union" \
+    "pretty_structs=True, unions=False" \
+    "\\{\[\r\n\]+  the_union = \\{\.\.\.\\}\[\r\n\]+\\}"
+
+  check_format_string "a_symbol_pointer" \
+    "symbols=False, format='d'" \
+    "\[0-9\]+"
+
+  if { $current_lang == "c++" } {
+    check_format_string "a_point_t_ref" \
+      "deref_refs=True, actual_objects=True, raw=True" \
+      "${default_ref_regexp}: \\{x = 42, y = 12\\}"
+
+    check_format_string "a_base_ref" \
+      "deref_refs=True, static_members=False" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42\\}"
+  }
+}
+
+# Test passing invalid arguments to gdb.Value.format_string.
+proc test_invalid_args {} {
+  check_format_string \
+    "a_point_t" \
+    "12" \
+    "TypeError: format_string\\(\\) takes 0 positional arguments but 1 were given.*"
+
+  check_format_string \
+    "a_point_t" \
+    "invalid=True" \
+    "TypeError: 'invalid' is an invalid keyword argument for this function.*"
+
+  check_format_string \
+    "a_point_t" \
+    "raw='hello'" \
+    "TypeError: argument 1 must be bool, not str.*"
+
+  check_format_string \
+    "a_point_t" \
+    "format='xd'" \
+    "ValueError: a single character is required.*"
+}
+
+# Run all the tests in common for both C and C++.
+proc test_all_common {} {
+  # No options.
+  test_no_opts
+  # Single options set to True/False.
+  test_raw
+  test_pretty_arrays
+  test_pretty_structs
+  test_array_indexes
+  test_symbols
+  test_unions
+  test_deref_refs
+  test_actual_objects
+  test_static_members
+  test_max_elements
+  test_repeat_threshold
+  test_format
+  # Multiple options mixed together.
+  test_mixed
+  # Various error conditions.
+  test_invalid_args
+}
+
+# The current language ("c" or "c++" while running tests).
+set current_lang ""
+
+with_test_prefix "format_string" {
+  # Perform C Tests.
+  if { [build_inferior "${binfile}" "c"] == 0 } {
+    with_test_prefix "lang_c" {
+      set current_lang "c"
+      prepare_gdb "${binfile}"
+      test_all_common
+    }
+  }
+
+  # Perform C++ Tests.
+  if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
+    with_test_prefix "lang_cpp" {
+      set current_lang "c++"
+      prepare_gdb "${binfile}-cxx"
+      test_all_common
+    }
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/gdb.python/py-format-string.py
new file mode 100644
index 0000000000..c2ad88e862
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2008-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests python pretty
+# printers.
+
+import gdb
+
+class PointPrinter (object):
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return 'Pretty Point (%s, %s)' % (self.val['x'], self.val['y'])
+
+def test_lookup_function (val):
+    "Look-up and return a pretty-printer that can print val."
+
+    # Get the type.
+    type = val.type
+
+    # If it points to a reference, get the reference.
+    if type.code == gdb.TYPE_CODE_REF:
+        type = type.target ()
+
+    # Get the unqualified type, stripped of typedefs.
+    type = type.unqualified ().strip_typedefs ()
+
+    # Get the type name.
+    typename = type.tag
+
+    if typename == 'point':
+        return PointPrinter (val)
+
+    return None
+
+gdb.pretty_printers.append (test_lookup_function)
-- 
2.19.2


[-- Attachment #3: Type: text/plain, Size: 23 bytes --]



-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3] Add gdb.Value.format_string ()
       [not found]       ` <D8FDF6CA-2755-4AC2-BE63-A76311F566E6@undo.io>
@ 2019-03-18 20:00         ` Eli Zaretskii
  2019-03-18 21:49           ` [PATCH v4] " Marco Barisione
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2019-03-18 20:00 UTC (permalink / raw)
  To: Marco Barisione; +Cc: kevinb, gdb-patches, tom

> From: Marco Barisione <mbarisione@undo.io>
> Date: Mon, 18 Mar 2019 18:14:35 +0000
> Cc: gdb-patches@sourceware.org,
>  Eli Zaretskii <eliz@gnu.org>,
>  Tom Tromey <tom@tromey.com>
> 
> +when using the @code{format_string()} method or the @code{str()}
                        ^^^^^^^^^^^^^^^                      ^^^^^
This part is still not fixed.

Thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4] Add gdb.Value.format_string ()
  2019-03-18 20:00         ` [PATCH v3] Add gdb.Value.format_string () Eli Zaretskii
@ 2019-03-18 21:49           ` Marco Barisione
  2019-03-19  6:48             ` Eli Zaretskii
  2019-03-26 12:07             ` [PING][PATCH " Marco Barisione
  0 siblings, 2 replies; 16+ messages in thread
From: Marco Barisione @ 2019-03-18 21:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Kevin Buettner, gdb-patches, tom

[-- Attachment #1: Type: text/plain, Size: 534 bytes --]

On 18 Mar 2019, at 20:00, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Marco Barisione <mbarisione@undo.io>
>> Date: Mon, 18 Mar 2019 18:14:35 +0000
>> Cc: gdb-patches@sourceware.org,
>> Eli Zaretskii <eliz@gnu.org>,
>> Tom Tromey <tom@tromey.com>
>> 
>> +when using the @code{format_string()} method or the @code{str()}
>                        ^^^^^^^^^^^^^^^                      ^^^^^
> This part is still not fixed.

Sorry, I missed it as I was looking for a space before the “(”.

Here’s an updated patch.


[-- Attachment #2: 0001-Add-gdb.Value.format_string.patch --]
[-- Type: application/octet-stream, Size: 53058 bytes --]

From 25235fdf13964209c5c4e6049b6e4ef4fac856e4 Mon Sep 17 00:00:00 2001
From: Marco Barisione <mbarisione@undo.io>
Date: Mon, 18 Mar 2019 17:59:03 +0000
Subject: [PATCH] Add gdb.Value.format_string ()

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

This patch introduces a new format_string () method to gdb.Value which
allows specifying more formatting options, thus giving access to more
features provided by the internal C function common_val_print ().

gdb/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Add gdb.Value.format_string ().
	* python/py-value.c (copy_py_bool_obj):
	(valpy_format_string): Add gdb.Value.format_string ().
	* NEWS: Document the addition of gdb.Value.format_string ().

gdb/doc/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	* python.texi (Values From Inferior): Document
	gdb.Value.format_string ().

gdb/testsuite/ChangeLog:

2019-03-18  Marco Barisione  <mbarisione@undo.io>

	Test gdb.Value.format_string ().
	* gdb.python/py-format-string.exp: New test.
	* gdb.python/py-format-string.c: New file.
	* gdb.python/py-format-string.py: New file.
---
 gdb/NEWS                                      |   6 +
 gdb/doc/python.texi                           |  80 ++
 gdb/python/py-value.c                         | 162 +++
 gdb/testsuite/gdb.python/py-format-string.c   | 118 +++
 gdb/testsuite/gdb.python/py-format-string.exp | 958 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-format-string.py  |  49 +
 6 files changed, 1373 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.c
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.exp
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.py

diff --git a/gdb/NEWS b/gdb/NEWS
index c45b313406..e4d9ae0fea 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -231,6 +231,12 @@ before Windows XP.
   ** The gdb.Value type has a new constructor, which is used to construct a
      gdb.Value from a Python buffer object and a gdb.Type.
 
+  ** The gdb.Value type has a new method 'format_string' which returns a
+     string representing the value.  The formatting is controlled by the
+     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
+     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
+     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
+
 * Configure changes
 
 --enable-ubsan
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index b02a154ce1..b03cd16673 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -864,6 +864,86 @@ Like @code{Value.cast}, but works as if the C@t{++} @code{reinterpret_cast}
 operator were used.  Consult a C@t{++} reference for details.
 @end defun
 
+@defun Value.format_string (...)
+Convert a @code{gdb.Value} to a string, similarly to what the @code{print}
+command does.  Invoked with no arguments, this is equivalent to calling
+the @code{str} function on the @code{gdb.Value}.  The representation of
+the same value may change across different versions of @value{GDBN}, so
+you shouldn't, for instance, parse the strings returned by this method.
+
+All the arguments are keyword only.  If an argument is not specified, the
+current global default setting is used.
+
+@table @code
+@item raw
+@code{True} if pretty-printers (@pxref{Pretty Printing}) should not be
+used to format the value.  @code{False} if enabled pretty-printers
+matching the type represented by the @code{gdb.Value} should be used to
+format it.
+
+@item pretty_arrays
+@code{True} if arrays should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print array} in
+@ref{Print Settings}).
+
+@item pretty_structs
+@code{True} if structs should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print pretty} in
+@ref{Print Settings}).
+
+@item array_indexes
+@code{True} if array indexes should be included in the string
+representation of arrays, @code{False} if they shouldn't (see @code{set
+print array-indexes} in @ref{Print Settings}).
+
+@item symbols
+@code{True} if the string representation of a pointer should include the
+corresponding symbol name (if one exists), @code{False} if it shouldn't
+(see @code{set print symbol} in @ref{Print Settings}).
+
+@item unions
+@code{True} if unions which are contained in other structures or unions
+should be expanded, @code{False} if they shouldn't (see @code{set print
+union} in @ref{Print Settings}).
+
+@item deref_refs
+@code{True} if C@t{++} references should be resolved to the value they
+refer to, @code{False} (the default) if they shouldn't.  Note that, unlike
+for the @code{print} command, references are not automatically expanded
+when using the @code{format_string} method or the @code{str}
+function.  There is no global @code{print} setting to change the default
+behaviour.
+
+@item actual_objects
+@code{True} if the representation of a pointer to an object should
+identify the @emph{actual} (derived) type of the object rather than the
+@emph{declared} type, using the virtual function table.  @code{False} if
+the @emph{declared} type should be used.  (See @code{set print object} in
+@ref{Print Settings}).
+
+@item static_fields
+@code{True} if static members should be included in the string
+representation of a C@t{++} object, @code{False} if they shouldn't (see
+@code{set print static-members} in @ref{Print Settings}).
+
+@item max_elements
+Number of array elements to print, or @code{0} to print an unlimited
+number of elements (see @code{set print elements} in @ref{Print
+Settings}).
+
+@item repeat_threshold
+Set the threshold for suppressing display of repeated array elements, or
+@code{0} to represent all elements, even if repeated.  (See @code{set
+print repeats} in @ref{Print Settings}).
+
+@item format
+A string containing a single character representing the format to use for
+the returned string.  For instance, @code{'x'} is equivalent to using the
+@value{GDBN} command @code{print} with the @code{/x} option and formats
+the value as a hexadecimal number.
+@end table
+@end defun
+
 @defun Value.string (@r{[}encoding@r{[}, errors@r{[}, length@r{]]]})
 If this @code{gdb.Value} represents a string, then this method
 converts the contents to a Python string.  Otherwise, this method will
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index dd6a536b6a..17f85d97d5 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -588,6 +588,163 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
 			   encoding, errors);
 }
 
+/* Given a Python object, copy its truth value to a C int (the value
+   pointed by dest).
+   If src_obj is NULL, then *dest is not modified.
+
+   Return true in case of success (including src_obj being NULL), false
+   in case of error.  */
+
+static bool
+copy_py_bool_obj (int *dest, PyObject *src_obj)
+{
+  if (src_obj)
+    {
+      int cmp = PyObject_IsTrue (src_obj);
+      if (cmp < 0)
+	return false;
+      *dest = cmp;
+    }
+
+  return true;
+}
+
+/* Implementation of gdb.Value.format_string (...) -> string.
+   Return Unicode string with value contents formatted using the
+   keyword-only arguments.  */
+
+static PyObject *
+valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] = {
+      /* Basic C/C++ options.  */
+      "raw",			/* See the /r option to print.  */
+      "pretty_arrays",		/* See set print array on|off.  */
+      "pretty_structs",		/* See set print pretty on|off.  */
+      "array_indexes",		/* See set print array-indexes on|off.  */
+      "symbols",		/* See set print symbol on|off.  */
+      "unions",			/* See set print union on|off.  */
+      /* C++ options.  */
+      "deref_refs",		/* No corresponding setting.  */
+      "actual_objects",		/* See set print object on|off.  */
+      "static_members",		/* See set print static-members on|off.  */
+      /* C non-bool options.  */
+      "max_elements", 		/* See set print elements N.  */
+      "repeat_threshold",	/* See set print repeats.  */
+      "format",			/* The format passed to the print command.  */
+      NULL };
+
+  /* This function has too many arguments to be useful as positionals, so
+     the user should specify them all as keyword arguments.
+     Python 3.3 and later have a way to specify it (both in C and Python
+     itself), but we could be compiled with older versions, so we just
+     check that the args tuple is empty.  */
+  Py_ssize_t positional_count = PyObject_Length (args);
+  if (positional_count < 0)
+    return NULL;
+  else if (positional_count > 0)
+    {
+      /* This matches the error message that Python 3.3 raises when
+	 passing positionals to functions expecting keyword-only
+	 arguments.  */
+      PyErr_Format (PyExc_TypeError,
+		    "format_string() takes 0 positional arguments but %zu were given",
+		    positional_count);
+      return NULL;
+    }
+
+  struct value_print_options opts;
+  get_user_print_options (&opts);
+  opts.deref_ref = 0;
+
+  /* We need objects for booleans as the "p" flag for bools is new in
+     Python 3.3.  */
+  PyObject *raw_obj = NULL;
+  PyObject *pretty_arrays_obj = NULL;
+  PyObject *pretty_structs_obj = NULL;
+  PyObject *array_indexes_obj = NULL;
+  PyObject *symbols_obj = NULL;
+  PyObject *unions_obj = NULL;
+  PyObject *deref_refs_obj = NULL;
+  PyObject *actual_objects_obj = NULL;
+  PyObject *static_members_obj = NULL;
+  char *format = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (args,
+					kw,
+					"|O!O!O!O!O!O!O!O!O!IIs",
+					keywords,
+					&PyBool_Type, &raw_obj,
+					&PyBool_Type, &pretty_arrays_obj,
+					&PyBool_Type, &pretty_structs_obj,
+					&PyBool_Type, &array_indexes_obj,
+					&PyBool_Type, &symbols_obj,
+					&PyBool_Type, &unions_obj,
+					&PyBool_Type, &deref_refs_obj,
+					&PyBool_Type, &actual_objects_obj,
+					&PyBool_Type, &static_members_obj,
+					&opts.print_max,
+					&opts.repeat_count_threshold,
+					&format))
+    return NULL;
+
+  /* Set boolean arguments.  */
+  if (!copy_py_bool_obj (&opts.raw, raw_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_arrays, pretty_arrays_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_structs, pretty_structs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.print_array_indexes, array_indexes_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.symbol_print, symbols_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.unionprint, unions_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.deref_ref, deref_refs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.objectprint, actual_objects_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.static_field_print, static_members_obj))
+    return NULL;
+
+  /* Numeric arguments for which 0 means unlimited (which we represent as
+     UINT_MAX).  */
+  if (opts.print_max == 0)
+    opts.print_max = UINT_MAX;
+  if (opts.repeat_count_threshold == 0)
+    opts.repeat_count_threshold = UINT_MAX;
+
+  /* Other arguments.  */
+  if (format != NULL)
+    {
+      if (strlen (format) == 1)
+	opts.format = format[0];
+      else
+	{
+	  /* Mimic the message on standard Python ones for similar
+	     errors.  */
+	  PyErr_SetString (PyExc_ValueError,
+			   "a single character is required");
+	  return NULL;
+	}
+    }
+
+  string_file stb;
+
+  TRY
+    {
+      common_val_print (((value_object *) self)->value, &stb, 0,
+			&opts, python_language);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return PyUnicode_Decode (stb.c_str (), stb.size (), host_charset (), NULL);
+}
+
 /* A helper function that implements the various cast operators.  */
 
 static PyObject *
@@ -1944,6 +2101,11 @@ Return a lazy string representation of the value." },
 Return Unicode string representation of the value." },
   { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
     "Fetches the value from the inferior, if it was lazy." },
+  { "format_string", (PyCFunction) valpy_format_string,
+    METH_VARARGS | METH_KEYWORDS,
+    "format_string (...) -> string\n\
+Return a string representation of the value using the specified\n\
+formatting options" },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-format-string.c b/gdb/testsuite/gdb.python/py-format-string.c
new file mode 100644
index 0000000000..120ecce989
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.c
@@ -0,0 +1,118 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+typedef struct point
+{
+  int x;
+  int y;
+} point_t;
+
+typedef union
+{
+  int an_int;
+  char a_char;
+} union_t;
+
+typedef struct
+{
+  union_t the_union;
+} struct_union_t;
+
+typedef enum
+{
+  ENUM_FOO,
+  ENUM_BAR,
+} enum_t;
+
+typedef void (*function_t) (int);
+
+static void
+my_function(int n)
+{
+}
+
+#ifdef __cplusplus
+
+struct Base
+{
+  Base (int a_) : a (a_) {}
+
+  virtual int get_number () { return a; }
+
+  int a;
+
+  static int a_static_member;
+};
+
+int Base::a_static_member = 2019;
+
+struct Deriv : Base
+{
+  Deriv (int b_) : Base (42), b (b_) {}
+
+  virtual int get_number () { return b; }
+
+  int b;
+};
+
+#endif
+
+int global_symbol = 42;
+
+int
+main ()
+{
+  point_t a_point_t = { 42, 12 };
+  point_t *a_point_t_pointer = &a_point_t;
+#ifdef __cplusplus
+  point_t &a_point_t_ref = a_point_t;
+#endif
+  struct point another_point = { 123, 456 };
+
+  struct_union_t a_struct_with_union;
+  a_struct_with_union.the_union.an_int = 42;
+
+  enum_t an_enum = ENUM_BAR;
+
+  const char *a_string = "hello world";
+  const char *a_binary_string = "hello\0world";
+  const char a_binary_string_array[] = "hello\0world";
+
+  const int letters_repeat = 10;
+  char a_big_string[26 * letters_repeat + 1];
+  a_big_string[26 * letters_repeat] = '\0';
+  for (int i = 0; i < letters_repeat; i++)
+    for (char c = 'A'; c <= 'Z'; c++)
+      a_big_string[i * 26 + c - 'A'] = c;
+
+  int an_array[] = { 2, 3, 5 };
+
+  int an_array_with_repetition[] = {
+    1,					/*  1 time.   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 12 times.  */
+    5, 5, 5,				/*  3 times   */
+    };
+
+  int *a_symbol_pointer = &global_symbol;
+
+#ifdef __cplusplus
+  Deriv a_deriv (123);
+  Base &a_base_ref = a_deriv;
+#endif
+
+  return 0; /* break here */
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
new file mode 100644
index 0000000000..4dbe3f80fa
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -0,0 +1,958 @@
+# Copyright (C) 2009-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Value.format_string () method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [get_compiler_info c++] {
+    return -1
+}
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc prepare_gdb {exefile} {
+  global srcdir subdir srcfile testfile hex
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+  gdb_load ${exefile}
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  if ![runto_main] then {
+      perror "couldn't run to breakpoint"
+      return
+  }
+
+  # Load the pretty printer.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+  runto_bp "break here"
+}
+
+# Set breakpoint and run to that breakpoint.
+proc runto_bp {bp} {
+  gdb_breakpoint [gdb_get_line_number $bp]
+  gdb_continue_to_breakpoint $bp
+}
+
+# Set an option using the GDB command in $set_cmd, execute $body, and then
+# restore the option using the GDB command in $unset_cmd.
+proc with_temp_option { set_cmd unset_cmd body } {
+  with_test_prefix $set_cmd {
+    gdb_test "$set_cmd" ".*"
+    uplevel 1 $body
+    gdb_test "$unset_cmd" ".*"
+  }
+}
+
+# A regular expression for a pointer.
+set default_pointer_regexp "0x\[a-fA-F0-9\]+"
+
+# A regular expression for a non-expanded C++ reference.
+#
+# Stringifying a C++ reference produces an address preceeded by a "@" in
+# Python, but, by default, the C++ reference/class is expanded by the
+# GDB print command.
+set default_ref_regexp "@${default_pointer_regexp}"
+
+# The whole content of the C variable a_big_string, i.e. the whole English
+# alphabet repeated 10 times.
+set whole_big_string ""
+set alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+for {set i 0} {$i < 10} {incr i} {
+  append whole_big_string $alphabet
+}
+unset alphabet
+
+# Produces a potentially cut down version of $whole_big_string like GDB
+# would represent it.
+# $max is the maximum number of characters allowed in the string (but
+# the return value may contain more to accound for the extra quotes and
+# "..." added by GDB).
+proc get_cut_big_string { max } {
+  global whole_big_string
+
+  set whole_size [string length $whole_big_string]
+  if { $max > $whole_size } {
+    return "\"${whole_big_string}\""
+  }
+
+  set cut_string [string range $whole_big_string 0 [expr $max - 1]]
+  return "\"${cut_string}\"..."
+}
+
+# A dictionary mapping from C variable names to their default string
+# representation when using str () or gdb.Value.format_string () with
+# no arguments.
+# This usually matches what the print command prints if used with no
+# options, except for C++ references which are not expanded by
+# default in Python.  See the comment above $default_ref_regexp.
+set default_regexp_dict [dict create \
+  "a_point_t"			"Pretty Point \\(42, 12\\)" \
+  "a_point_t_pointer"		$default_pointer_regexp \
+  "a_point_t_ref"		"Pretty Point \\(42, 12\\)" \
+  "another_point"		"Pretty Point \\(123, 456\\)" \
+  "a_struct_with_union"		"\\{the_union = \\{an_int = 42, a_char = 42 '\\*'\\}\\}" \
+  "an_enum"			"ENUM_BAR" \
+  "a_string"			"${default_pointer_regexp} \"hello world\"" \
+  "a_binary_string"		"${default_pointer_regexp} \"hello\"" \
+  "a_binary_string_array"	"\"hello\\\\000world\"" \
+  "a_big_string"		[get_cut_big_string 200] \
+  "an_array"			"\\{2, 3, 5\\}" \
+  "an_array_with_repetition"	"\\{1, 3 <repeats 12 times>, 5, 5, 5\\}" \
+  "a_symbol_pointer"		"${default_pointer_regexp} <global_symbol>" \
+  "a_base_ref"			"${default_ref_regexp}" \
+  ]
+
+# A sentinel value to pass to function to get them to use a default value
+# instead.
+# Note that we cannot use $undefined for default arguments in function
+# definitions as we would just get the literal "$undefined" string, so
+# we need to repeat the string.
+set undefined "\000UNDEFINED\000"
+
+# Return $value if it's not $undefined, otherwise return the default value
+# (from $default_regexp_dict) for the variable $var.
+proc get_value_or_default { var value } {
+  global undefined
+  if { $value != $undefined } {
+    return $value
+  }
+
+  global default_regexp_dict
+  return [dict get $default_regexp_dict $var]
+}
+
+# Check that using gdb.Value.format_string on the value representing the
+# variable $var produces $expected.
+proc check_format_string {
+	var
+	opts
+	{ expected "\000UNDEFINED\000" }
+	{ name "\000UNDEFINED\000" }
+  } {
+  global undefined
+
+  set expected [get_value_or_default $var $expected]
+  if { $name == $undefined } {
+    set name "${var} with option ${opts}"
+  }
+
+  gdb_test \
+    "python print (gdb.parse_and_eval ('${var}').format_string (${opts}))" \
+    $expected \
+    $name
+}
+
+# Check that printing $var with no options set, produces the expected
+# output.
+proc check_var_with_no_opts {
+	var
+	{ expected "\000UNDEFINED\000" }
+  } {
+  set expected [get_value_or_default $var $expected]
+
+  with_test_prefix "${var}" {
+    check_format_string \
+      $var \
+      "" \
+      $expected \
+      "no opts"
+    # str () should behave like gdb.Value.format_string () with no args.
+    gdb_test \
+      "python print (str (gdb.parse_and_eval ('${var}')))" \
+      $expected \
+      "str"
+  }
+}
+
+# Check that printing $var with $opt set to True and set to False,
+# produces the expected output.
+proc check_var_with_bool_opt {
+	opt
+	var
+	{ true_expected  "\000UNDEFINED\000" }
+	{ false_expected "\000UNDEFINED\000" }
+  } {
+  set true_expected  [get_value_or_default $var $true_expected]
+  set false_expected [get_value_or_default $var $false_expected]
+
+  with_test_prefix "${var} with option ${opt}" {
+    # Option set to True.
+    check_format_string \
+      $var \
+      "${opt}=True" \
+      $true_expected \
+      "${opt}=true"
+    # Option set to False.
+    check_format_string \
+      $var \
+      "${opt}=False" \
+      $false_expected \
+      "${opt}=false"
+  }
+}
+
+# Test gdb.Value.format_string with no options.
+proc test_no_opts {} {
+  global current_lang
+
+  check_var_with_no_opts "a_point_t"
+  check_var_with_no_opts "a_point_t_pointer"
+  check_var_with_no_opts "another_point"
+  check_var_with_no_opts "a_struct_with_union"
+  check_var_with_no_opts "an_enum"
+  check_var_with_no_opts "a_string"
+  check_var_with_no_opts "a_binary_string"
+  check_var_with_no_opts "a_binary_string_array"
+  check_var_with_no_opts "a_big_string"
+  check_var_with_no_opts "an_array"
+  check_var_with_no_opts "an_array_with_repetition"
+  check_var_with_no_opts "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_no_opts "a_point_t_ref"
+    check_var_with_no_opts "a_base_ref"
+  }
+}
+
+# Test the raw option for gdb.Value.format_string.
+proc test_raw {} {
+  global current_lang
+  global default_ref_regexp
+
+  check_var_with_bool_opt "raw" "a_point_t" \
+    "{x = 42, y = 12}"
+  check_var_with_bool_opt "raw" "a_point_t_pointer"
+  check_var_with_bool_opt "raw" "another_point" \
+    "{x = 123, y = 456}"
+  check_var_with_bool_opt "raw" "a_struct_with_union"
+  check_var_with_bool_opt "raw" "an_enum"
+  check_var_with_bool_opt "raw" "a_string"
+  check_var_with_bool_opt "raw" "a_binary_string"
+  check_var_with_bool_opt "raw" "a_binary_string_array"
+  check_var_with_bool_opt "raw" "a_big_string"
+  check_var_with_bool_opt "raw" "an_array"
+  check_var_with_bool_opt "raw" "an_array_with_repetition"
+  check_var_with_bool_opt "raw" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "raw" "a_point_t_ref" \
+      ${default_ref_regexp}
+    check_var_with_bool_opt "raw" "a_base_ref"
+  }
+
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "raw" "a_point_t" \
+      "{x = 42, y = 12}" \
+      "{x = 42, y = 12}"
+  }
+}
+
+# Test the pretty_arrays option for gdb.Value.format_string.
+proc test_pretty_arrays {} {
+  global current_lang
+
+  set an_array_pretty "  \\{2,\[\r\n\]+  3,\[\r\n\]+  5\\}"
+  set an_array_with_repetition_pretty \
+    "  \\{1,\[\r\n\]+  3 <repeats 12 times>,\[\r\n\]+  5,\[\r\n\]+  5,\[\r\n\]+  5\\}"
+
+  check_var_with_bool_opt "pretty_arrays" "a_point_t"
+  check_var_with_bool_opt "pretty_arrays" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_arrays" "another_point"
+  check_var_with_bool_opt "pretty_arrays" "a_struct_with_union"
+  check_var_with_bool_opt "pretty_arrays" "an_enum"
+  check_var_with_bool_opt "pretty_arrays" "a_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_arrays" "a_big_string"
+  check_var_with_bool_opt "pretty_arrays" "an_array" \
+    $an_array_pretty
+  check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+    $an_array_with_repetition_pretty
+  check_var_with_bool_opt "pretty_arrays" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_arrays" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_arrays" "a_base_ref"
+  }
+
+  with_temp_option "set print array on" "set print array off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array" \
+      $an_array_pretty
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+  }
+}
+
+# Test the pretty_structs option for gdb.Value.format_string.
+proc test_pretty_structs {} {
+  global current_lang
+
+  set a_struct_with_union_pretty \
+    "\\{\[\r\n\]+  the_union = \\{\[\r\n\]+    an_int = 42, \[\r\n\]+    a_char = 42 '\\*'\[\r\n\]+  \\}\[\r\n\]+\\}"
+  # Note the space after the colon!                        ^ here
+
+  check_var_with_bool_opt "pretty_structs" "a_point_t"
+  check_var_with_bool_opt "pretty_structs" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_structs" "another_point"
+  check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+    $a_struct_with_union_pretty
+  check_var_with_bool_opt "pretty_structs" "an_enum"
+  check_var_with_bool_opt "pretty_structs" "a_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_structs" "a_big_string"
+  check_var_with_bool_opt "pretty_structs" "an_array"
+  check_var_with_bool_opt "pretty_structs" "an_array_with_repetition"
+  check_var_with_bool_opt "pretty_structs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_structs" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_structs" "a_base_ref"
+  }
+
+  with_temp_option "set print structs on" "set print structs off" {
+    check_var_with_no_opts "a_struct_with_union"
+    check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+      $a_struct_with_union_pretty
+  }
+
+  # point_t is usually printed through the pretty printer.
+  # Try disabling it.
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "pretty_structs" "a_point_t" \
+      "\\{\[\r\n\]+  x = 42, *\[\r\n\]+  y = 12\[\r\n\]+\\}" \
+      "{x = 42, y = 12}" \
+  }
+}
+
+# Test the array_indexes option for gdb.Value.format_string.
+proc test_array_indexes {} {
+  global current_lang
+
+  set an_array_with_indexes "\\{\\\[0\\\] = 2, \\\[1\\\] = 3, \\\[2\\\] = 5\\}"
+  set an_array_with_repetition_with_indexes \
+    "\\{\\\[0\\\] = 1, \\\[1\\\] = 3 <repeats 12 times>, \\\[13\\\] = 5, \\\[14\\\] = 5, \\\[15\\\] = 5\\}"
+
+  check_var_with_bool_opt "array_indexes" "a_point_t"
+  check_var_with_bool_opt "array_indexes" "a_point_t_pointer"
+  check_var_with_bool_opt "array_indexes" "another_point"
+  check_var_with_bool_opt "array_indexes" "a_struct_with_union"
+  check_var_with_bool_opt "array_indexes" "an_enum"
+  check_var_with_bool_opt "array_indexes" "a_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string_array"
+  check_var_with_bool_opt "array_indexes" "a_big_string"
+  check_var_with_bool_opt "array_indexes" "an_array" \
+    $an_array_with_indexes
+  check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+    $an_array_with_repetition_with_indexes
+  check_var_with_bool_opt "array_indexes" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "array_indexes" "a_point_t_ref"
+    check_var_with_bool_opt "array_indexes" "a_base_ref"
+  }
+
+  with_temp_option \
+	"set print array-indexes on" \
+	"set print array-indexes off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array" \
+      $an_array_with_indexes
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+  }
+}
+
+# Test the symbols option for gdb.Value.format_string.
+proc test_symbols {} {
+  global undefined
+  global current_lang
+  global default_pointer_regexp
+
+  check_var_with_bool_opt "symbols" "a_point_t"
+  check_var_with_bool_opt "symbols" "a_point_t_pointer"
+  check_var_with_bool_opt "symbols" "another_point"
+  check_var_with_bool_opt "symbols" "a_struct_with_union"
+  check_var_with_bool_opt "symbols" "an_enum"
+  check_var_with_bool_opt "symbols" "a_string"
+  check_var_with_bool_opt "symbols" "a_binary_string"
+  check_var_with_bool_opt "symbols" "a_binary_string_array"
+  check_var_with_bool_opt "symbols" "a_big_string"
+  check_var_with_bool_opt "symbols" "an_array"
+  check_var_with_bool_opt "symbols" "an_array_with_repetition"
+  check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+    $undefined \
+    $default_pointer_regexp
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "symbols" "a_point_t_ref"
+    check_var_with_bool_opt "symbols" "a_base_ref"
+  }
+
+  with_temp_option "set print symbol off" "set print symbol on" {
+    check_var_with_no_opts "a_symbol_pointer" \
+      $default_pointer_regexp
+    check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+      $undefined \
+      $default_pointer_regexp
+  }
+}
+
+# Test the unions option for gdb.Value.format_string.
+proc test_unions {} {
+  global undefined
+  global current_lang
+
+  check_var_with_bool_opt "unions" "a_point_t"
+  check_var_with_bool_opt "unions" "a_point_t_pointer"
+  check_var_with_bool_opt "unions" "another_point"
+  check_var_with_bool_opt "unions" "a_struct_with_union" \
+    $undefined \
+    "\\{the_union = \\{...\\}\\}"
+  check_var_with_bool_opt "unions" "an_enum"
+  check_var_with_bool_opt "unions" "a_string"
+  check_var_with_bool_opt "unions" "a_binary_string"
+  check_var_with_bool_opt "unions" "a_binary_string_array"
+  check_var_with_bool_opt "unions" "a_big_string"
+  check_var_with_bool_opt "unions" "an_array"
+  check_var_with_bool_opt "unions" "an_array_with_repetition"
+  check_var_with_bool_opt "unions" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "unions" "a_point_t_ref"
+    check_var_with_bool_opt "unions" "a_base_ref"
+  }
+
+  with_temp_option "set print union off" "set print union on" {
+    check_var_with_no_opts "a_struct_with_union" \
+      "\\{the_union = \\{...\\}\\}"
+    check_var_with_bool_opt "unions" "a_struct_with_union" \
+      $undefined \
+      "\\{the_union = \\{...\\}\\}"
+  }
+}
+
+# Test the deref_refs option for gdb.Value.format_string.
+proc test_deref_refs {} {
+  global current_lang
+  global default_pointer_regexp
+  global default_ref_regexp
+
+  check_var_with_bool_opt "deref_refs" "a_point_t"
+  check_var_with_bool_opt "deref_refs" "a_point_t_pointer"
+  check_var_with_bool_opt "deref_refs" "another_point"
+  check_var_with_bool_opt "deref_refs" "a_struct_with_union"
+  check_var_with_bool_opt "deref_refs" "an_enum"
+  check_var_with_bool_opt "deref_refs" "a_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string_array"
+  check_var_with_bool_opt "deref_refs" "a_big_string"
+  check_var_with_bool_opt "deref_refs" "an_array"
+  check_var_with_bool_opt "deref_refs" "an_array_with_repetition"
+  check_var_with_bool_opt "deref_refs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "deref_refs" "a_point_t_ref"
+    check_var_with_bool_opt "deref_refs" "a_base_ref" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42, static a_static_member = 2019\\}"
+  }
+}
+
+# Test the actual_objects option for gdb.Value.format_string.
+proc test_actual_objects {} {
+  global current_lang
+
+  check_var_with_bool_opt "actual_objects" "a_point_t"
+  check_var_with_bool_opt "actual_objects" "a_point_t_pointer"
+  check_var_with_bool_opt "actual_objects" "another_point"
+  check_var_with_bool_opt "actual_objects" "a_struct_with_union"
+  check_var_with_bool_opt "actual_objects" "an_enum"
+  check_var_with_bool_opt "actual_objects" "a_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string_array"
+  check_var_with_bool_opt "actual_objects" "a_big_string"
+  check_var_with_bool_opt "actual_objects" "an_array"
+  check_var_with_bool_opt "actual_objects" "an_array_with_repetition"
+  check_var_with_bool_opt "actual_objects" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+    check_var_with_bool_opt "actual_objects" "a_base_ref"
+
+    with_temp_option "set print object on" "set print object off" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "actual_objects" "a_base_ref"
+    }
+  }
+}
+
+# Test the static_members option for gdb.Value.format_string.
+proc test_static_members {} {
+  global current_lang
+
+  check_var_with_bool_opt "static_members" "a_point_t"
+  check_var_with_bool_opt "static_members" "a_point_t_pointer"
+  check_var_with_bool_opt "static_members" "another_point"
+  check_var_with_bool_opt "static_members" "a_struct_with_union"
+  check_var_with_bool_opt "static_members" "an_enum"
+  check_var_with_bool_opt "static_members" "a_string"
+  check_var_with_bool_opt "static_members" "a_binary_string"
+  check_var_with_bool_opt "static_members" "a_binary_string_array"
+  check_var_with_bool_opt "static_members" "a_big_string"
+  check_var_with_bool_opt "static_members" "an_array"
+  check_var_with_bool_opt "static_members" "an_array_with_repetition"
+  check_var_with_bool_opt "static_members" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "static_members" "a_point_t_ref"
+    check_var_with_bool_opt "static_members" "a_base_ref"
+
+    with_temp_option \
+	"set print static-members off" \
+	"set print static-members on" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "static_members" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "static_members" "a_base_ref"
+    }
+  }
+}
+
+# Test the max_elements option for gdb.Value.format_string.
+proc test_max_elements {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 200 is the default maximum number of elements, so setting it should
+  # not change the output.
+  set opts "max_elements=200"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "max_elements=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    # This will print four characters instead of three, see
+    # <https://sourceware.org/bugzilla/show_bug.cgi?id=24331>.
+    check_format_string "a_binary_string_array" $opts \
+      "\"hell\"..."
+    check_format_string "a_big_string" $opts \
+      [get_cut_big_string 3]
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>...\\}"
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # mean no truncation.
+  foreach opts { "max_elements=1000" "max_elements=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts \
+        [get_cut_big_string 1000]
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print elements 4" "set print elements 200" {
+    check_format_string "a_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string_array" "" \
+      "\"hell\"..."
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>...\\}"
+  }
+}
+
+# Test the repeat_threshold option for gdb.Value.format_string.
+proc test_repeat_threshold {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 10 is the default threshold for repeated items, so setting it should
+  # not change the output.
+  set opts "repeat_threshold=10"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=1"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o world\""
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o\""
+    check_format_string "a_binary_string_array" $opts \
+      "\"he\", 'l' <repeats 2 times>, \"o\\\\000world\""
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 100 (we don't have that many repeated elements) and 0 (unlimited)
+  # should mean no truncation.
+  foreach opts { "repeat_threshold=100" "repeat_threshold=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+        "\\{1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5\\}"
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print repeats 1" "set print repeats 10" {
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+  }
+}
+
+# Test the format option for gdb.Value.format_string.
+proc test_format {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # Hexadecimal.
+  set opts "format='x'"
+  with_test_prefix $opts {
+    gdb_test "python print (gdb.Value (42).format_string (${opts}))" \
+      "0x2a" \
+      "42 with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 0x2a, a_char = 0x2a\\}\\}"
+    check_format_string "an_enum" $opts \
+      "0x1"
+    check_format_string "a_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{0x41, 0x42, 0x43, 0x44, 0x45, \[, x0-9a-f\]+\.\.\.\\}"
+    check_format_string "an_array" $opts \
+      "\\{0x2, 0x3, 0x5\\}"
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{0x1, 0x3 <repeats 12 times>, 0x5, 0x5, 0x5\\}"
+    check_format_string "a_symbol_pointer" $opts \
+      $default_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Decimal.
+  set opts "format='d'"
+  with_test_prefix $opts {
+    set decimal_pointer_regexp "\[0-9\]+"
+    gdb_test "python print (gdb.Value (0x2a).format_string (${opts}))" \
+      "42" \
+      "0x2a with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts \
+      $decimal_pointer_regexp
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 42, a_char = 42\\}\\}"
+    check_format_string "an_enum" $opts \
+      "1"
+    check_format_string "a_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{104, 101, 108, 108, 111, 0, 119, 111, 114, 108, 100, 0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{65, 66, 67, 68, 69, \[, 0-9\]+\.\.\.\\}"
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts \
+      $decimal_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+}
+
+# Test mixing options.
+proc test_mixed {} {
+  global current_lang
+  global default_ref_regexp
+  global default_pointer_regexp
+
+  check_format_string "a_point_t" \
+    "raw=True, format='x'" \
+    "\\{x = 0x2a, y = 0xc\\}"
+
+  check_format_string "an_array" \
+    "array_indexes=True, pretty_arrays=True" \
+    "  \\{\\\[0\\\] = 2,\[\r\n\]+  \\\[1\\\] = 3,\[\r\n\]+  \\\[2\\\] = 5\\}"
+
+  check_format_string "a_struct_with_union" \
+    "pretty_structs=True, unions=False" \
+    "\\{\[\r\n\]+  the_union = \\{\.\.\.\\}\[\r\n\]+\\}"
+
+  check_format_string "a_symbol_pointer" \
+    "symbols=False, format='d'" \
+    "\[0-9\]+"
+
+  if { $current_lang == "c++" } {
+    check_format_string "a_point_t_ref" \
+      "deref_refs=True, actual_objects=True, raw=True" \
+      "${default_ref_regexp}: \\{x = 42, y = 12\\}"
+
+    check_format_string "a_base_ref" \
+      "deref_refs=True, static_members=False" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42\\}"
+  }
+}
+
+# Test passing invalid arguments to gdb.Value.format_string.
+proc test_invalid_args {} {
+  check_format_string \
+    "a_point_t" \
+    "12" \
+    "TypeError: format_string\\(\\) takes 0 positional arguments but 1 were given.*"
+
+  check_format_string \
+    "a_point_t" \
+    "invalid=True" \
+    "TypeError: 'invalid' is an invalid keyword argument for this function.*"
+
+  check_format_string \
+    "a_point_t" \
+    "raw='hello'" \
+    "TypeError: argument 1 must be bool, not str.*"
+
+  check_format_string \
+    "a_point_t" \
+    "format='xd'" \
+    "ValueError: a single character is required.*"
+}
+
+# Run all the tests in common for both C and C++.
+proc test_all_common {} {
+  # No options.
+  test_no_opts
+  # Single options set to True/False.
+  test_raw
+  test_pretty_arrays
+  test_pretty_structs
+  test_array_indexes
+  test_symbols
+  test_unions
+  test_deref_refs
+  test_actual_objects
+  test_static_members
+  test_max_elements
+  test_repeat_threshold
+  test_format
+  # Multiple options mixed together.
+  test_mixed
+  # Various error conditions.
+  test_invalid_args
+}
+
+# The current language ("c" or "c++" while running tests).
+set current_lang ""
+
+with_test_prefix "format_string" {
+  # Perform C Tests.
+  if { [build_inferior "${binfile}" "c"] == 0 } {
+    with_test_prefix "lang_c" {
+      set current_lang "c"
+      prepare_gdb "${binfile}"
+      test_all_common
+    }
+  }
+
+  # Perform C++ Tests.
+  if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
+    with_test_prefix "lang_cpp" {
+      set current_lang "c++"
+      prepare_gdb "${binfile}-cxx"
+      test_all_common
+    }
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/gdb.python/py-format-string.py
new file mode 100644
index 0000000000..c2ad88e862
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2008-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests python pretty
+# printers.
+
+import gdb
+
+class PointPrinter (object):
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return 'Pretty Point (%s, %s)' % (self.val['x'], self.val['y'])
+
+def test_lookup_function (val):
+    "Look-up and return a pretty-printer that can print val."
+
+    # Get the type.
+    type = val.type
+
+    # If it points to a reference, get the reference.
+    if type.code == gdb.TYPE_CODE_REF:
+        type = type.target ()
+
+    # Get the unqualified type, stripped of typedefs.
+    type = type.unqualified ().strip_typedefs ()
+
+    # Get the type name.
+    typename = type.tag
+
+    if typename == 'point':
+        return PointPrinter (val)
+
+    return None
+
+gdb.pretty_printers.append (test_lookup_function)
-- 
2.19.2


[-- Attachment #3: Type: text/plain, Size: 23 bytes --]



-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4] Add gdb.Value.format_string ()
  2019-03-18 21:49           ` [PATCH v4] " Marco Barisione
@ 2019-03-19  6:48             ` Eli Zaretskii
  2019-03-26 12:07             ` [PING][PATCH " Marco Barisione
  1 sibling, 0 replies; 16+ messages in thread
From: Eli Zaretskii @ 2019-03-19  6:48 UTC (permalink / raw)
  To: Marco Barisione; +Cc: kevinb, gdb-patches, tom

> From: Marco Barisione <mbarisione@undo.io>
> Date: Mon, 18 Mar 2019 21:49:08 +0000
> Cc: Kevin Buettner <kevinb@redhat.com>,
>  gdb-patches@sourceware.org,
>  tom@tromey.com
> 
> On 18 Mar 2019, at 20:00, Eli Zaretskii <eliz@gnu.org> wrote:
> >> From: Marco Barisione <mbarisione@undo.io>
> >> Date: Mon, 18 Mar 2019 18:14:35 +0000
> >> Cc: gdb-patches@sourceware.org,
> >> Eli Zaretskii <eliz@gnu.org>,
> >> Tom Tromey <tom@tromey.com>
> >> 
> >> +when using the @code{format_string()} method or the @code{str()}
> >                        ^^^^^^^^^^^^^^^                      ^^^^^
> > This part is still not fixed.
> 
> Sorry, I missed it as I was looking for a space before the “(”.
> 
> Here’s an updated patch.

This version is fine with me, thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PING][PATCH v4] Add gdb.Value.format_string ()
  2019-03-18 21:49           ` [PATCH v4] " Marco Barisione
  2019-03-19  6:48             ` Eli Zaretskii
@ 2019-03-26 12:07             ` Marco Barisione
  1 sibling, 0 replies; 16+ messages in thread
From: Marco Barisione @ 2019-03-26 12:07 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Kevin Buettner, gdb-patches, Eli Zaretskii

Tom, ping? :)
I believe that Kevin and Eli are fine with my patch, but it was suggested you review it as well.

> On 18 Mar 2019, at 21:49, Marco Barisione <mbarisione@undo.io> wrote:
> 
> Sorry, I missed it as I was looking for a space before the “(”.
> 
> Here’s an updated patch.
> 
> <0001-Add-gdb.Value.format_string.patch>
> 
> -- 
> Marco Barisione
> 

-- 
Marco Barisione

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3] Add gdb.Value.format_string ()
  2019-03-18 18:17       ` [PATCH v3] " Marco Barisione
@ 2019-03-27 20:16         ` Tom Tromey
  2019-03-28 13:55           ` [PATCH v4] " Marco Barisione
  0 siblings, 1 reply; 16+ messages in thread
From: Tom Tromey @ 2019-03-27 20:16 UTC (permalink / raw)
  To: Marco Barisione; +Cc: Kevin Buettner, gdb-patches, Eli Zaretskii, Tom Tromey

>>>>> "Marco" == Marco Barisione <mbarisione@undo.io> writes:

Hi.  I apologize for the delay on this.  I am rather behind.

I only have two nits with this patch.  The rest looks very good, thank
you for doing this.

Marco> +  static const char *keywords[] = {

I think the brace should be on a new line.

Marco> +      NULL };

Here too.


This is ok with these changed.

Tom

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4] Add gdb.Value.format_string ()
  2019-03-27 20:16         ` Tom Tromey
@ 2019-03-28 13:55           ` Marco Barisione
  2019-03-29 15:03             ` Tom Tromey
  2019-04-12 20:18             ` New failures on gdb.python/py-format-string.exp - unix/-m32 (was: Re: [PATCH v4] Add gdb.Value.format_string ()) Sergio Durigan Junior
  0 siblings, 2 replies; 16+ messages in thread
From: Marco Barisione @ 2019-03-28 13:55 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Kevin Buettner, gdb-patches, Eli Zaretskii

[-- Attachment #1: Type: text/plain, Size: 955 bytes --]

On 27 Mar 2019, at 20:16, Tom Tromey <tom@tromey.com> wrote:
> I only have two nits with this patch.  The rest looks very good, thank
> you for doing this.

Thanks!

I’ve attached a patch which is identical to the previous one except for:
* Indentation fixed;
* The NEWS file update was moved to the “Since GDB 8.3” section;
* Test fixed to not expect a stray space between a comma and a newline
  any more (as the original behaviour was fixed by Andrew Burgess a few
  days ago).

> 
> Marco> +  static const char *keywords[] = {
> 
> I think the brace should be on a new line.
> 
> Marco> +      NULL };
> 
> Here too.

I’ve done this and indented the braces by two spaces and the array’s
content by another two spaces.
This seems to match my understanding of the GNU/GDB indentation style, but
I’m not 100% sure it’s the correct one as, in other places in the code
base, this kind of brace is not indented.



[-- Attachment #2: 0001-Add-gdb.Value.format_string.patch --]
[-- Type: application/octet-stream, Size: 52976 bytes --]

From fee7c991eb078db00b7231cbc11ed553dee67584 Mon Sep 17 00:00:00 2001
From: Marco Barisione <mbarisione@undo.io>
Date: Thu, 28 Mar 2019 13:08:49 +0000
Subject: [PATCH] Add gdb.Value.format_string ()

The str () function, called on a gdb.Value instance, produces a string
representation similar to what can be achieved with the print command,
but it doesn't allow to specify additional formatting settings, for
instance disabling pretty printers.

This patch introduces a new format_string () method to gdb.Value which
allows specifying more formatting options, thus giving access to more
features provided by the internal C function common_val_print ().

gdb/ChangeLog:

2019-03-28  Marco Barisione  <mbarisione@undo.io>

	Add gdb.Value.format_string ().
	* python/py-value.c (copy_py_bool_obj):
	(valpy_format_string): Add gdb.Value.format_string ().
	* NEWS: Document the addition of gdb.Value.format_string ().

gdb/doc/ChangeLog:

2019-03-28  Marco Barisione  <mbarisione@undo.io>

	* python.texi (Values From Inferior): Document
	gdb.Value.format_string ().

gdb/testsuite/ChangeLog:

2019-03-28  Marco Barisione  <mbarisione@undo.io>

	Test gdb.Value.format_string ().
	* gdb.python/py-format-string.exp: New test.
	* gdb.python/py-format-string.c: New file.
	* gdb.python/py-format-string.py: New file.
---
 gdb/NEWS                                      |   9 +
 gdb/doc/python.texi                           |  80 ++
 gdb/python/py-value.c                         | 164 +++
 gdb/testsuite/gdb.python/py-format-string.c   | 118 +++
 gdb/testsuite/gdb.python/py-format-string.exp | 957 ++++++++++++++++++
 gdb/testsuite/gdb.python/py-format-string.py  |  49 +
 6 files changed, 1377 insertions(+)
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.c
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.exp
 create mode 100644 gdb/testsuite/gdb.python/py-format-string.py

diff --git a/gdb/NEWS b/gdb/NEWS
index edcc9c951a..c0b5643714 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -10,6 +10,15 @@
 
 * Support for Pointer Authentication on AArch64 Linux.
 
+* Python API
+
+  ** The gdb.Value type has a new method 'format_string' which returns a
+     string representing the value.  The formatting is controlled by the
+     optional keyword arguments: 'raw', 'pretty_arrays', 'pretty_structs',
+     'array_indexes', 'symbols', 'unions', 'deref_refs', 'actual_objects',
+     'static_members', 'max_elements', 'repeat_threshold', and 'format'.
+
+
 *** Changes in GDB 8.3
 
 * GDB and GDBserver now support access to additional registers on
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 56c925d4dd..67165ac3ba 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -864,6 +864,86 @@ Like @code{Value.cast}, but works as if the C@t{++} @code{reinterpret_cast}
 operator were used.  Consult a C@t{++} reference for details.
 @end defun
 
+@defun Value.format_string (...)
+Convert a @code{gdb.Value} to a string, similarly to what the @code{print}
+command does.  Invoked with no arguments, this is equivalent to calling
+the @code{str} function on the @code{gdb.Value}.  The representation of
+the same value may change across different versions of @value{GDBN}, so
+you shouldn't, for instance, parse the strings returned by this method.
+
+All the arguments are keyword only.  If an argument is not specified, the
+current global default setting is used.
+
+@table @code
+@item raw
+@code{True} if pretty-printers (@pxref{Pretty Printing}) should not be
+used to format the value.  @code{False} if enabled pretty-printers
+matching the type represented by the @code{gdb.Value} should be used to
+format it.
+
+@item pretty_arrays
+@code{True} if arrays should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print array} in
+@ref{Print Settings}).
+
+@item pretty_structs
+@code{True} if structs should be pretty printed to be more convenient to
+read, @code{False} if they shouldn't (see @code{set print pretty} in
+@ref{Print Settings}).
+
+@item array_indexes
+@code{True} if array indexes should be included in the string
+representation of arrays, @code{False} if they shouldn't (see @code{set
+print array-indexes} in @ref{Print Settings}).
+
+@item symbols
+@code{True} if the string representation of a pointer should include the
+corresponding symbol name (if one exists), @code{False} if it shouldn't
+(see @code{set print symbol} in @ref{Print Settings}).
+
+@item unions
+@code{True} if unions which are contained in other structures or unions
+should be expanded, @code{False} if they shouldn't (see @code{set print
+union} in @ref{Print Settings}).
+
+@item deref_refs
+@code{True} if C@t{++} references should be resolved to the value they
+refer to, @code{False} (the default) if they shouldn't.  Note that, unlike
+for the @code{print} command, references are not automatically expanded
+when using the @code{format_string} method or the @code{str}
+function.  There is no global @code{print} setting to change the default
+behaviour.
+
+@item actual_objects
+@code{True} if the representation of a pointer to an object should
+identify the @emph{actual} (derived) type of the object rather than the
+@emph{declared} type, using the virtual function table.  @code{False} if
+the @emph{declared} type should be used.  (See @code{set print object} in
+@ref{Print Settings}).
+
+@item static_fields
+@code{True} if static members should be included in the string
+representation of a C@t{++} object, @code{False} if they shouldn't (see
+@code{set print static-members} in @ref{Print Settings}).
+
+@item max_elements
+Number of array elements to print, or @code{0} to print an unlimited
+number of elements (see @code{set print elements} in @ref{Print
+Settings}).
+
+@item repeat_threshold
+Set the threshold for suppressing display of repeated array elements, or
+@code{0} to represent all elements, even if repeated.  (See @code{set
+print repeats} in @ref{Print Settings}).
+
+@item format
+A string containing a single character representing the format to use for
+the returned string.  For instance, @code{'x'} is equivalent to using the
+@value{GDBN} command @code{print} with the @code{/x} option and formats
+the value as a hexadecimal number.
+@end table
+@end defun
+
 @defun Value.string (@r{[}encoding@r{[}, errors@r{[}, length@r{]]]})
 If this @code{gdb.Value} represents a string, then this method
 converts the contents to a Python string.  Otherwise, this method will
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index dd6a536b6a..1dcb60d72a 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -588,6 +588,165 @@ valpy_string (PyObject *self, PyObject *args, PyObject *kw)
 			   encoding, errors);
 }
 
+/* Given a Python object, copy its truth value to a C int (the value
+   pointed by dest).
+   If src_obj is NULL, then *dest is not modified.
+
+   Return true in case of success (including src_obj being NULL), false
+   in case of error.  */
+
+static bool
+copy_py_bool_obj (int *dest, PyObject *src_obj)
+{
+  if (src_obj)
+    {
+      int cmp = PyObject_IsTrue (src_obj);
+      if (cmp < 0)
+	return false;
+      *dest = cmp;
+    }
+
+  return true;
+}
+
+/* Implementation of gdb.Value.format_string (...) -> string.
+   Return Unicode string with value contents formatted using the
+   keyword-only arguments.  */
+
+static PyObject *
+valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
+{
+  static const char *keywords[] =
+    {
+      /* Basic C/C++ options.  */
+      "raw",			/* See the /r option to print.  */
+      "pretty_arrays",		/* See set print array on|off.  */
+      "pretty_structs",		/* See set print pretty on|off.  */
+      "array_indexes",		/* See set print array-indexes on|off.  */
+      "symbols",		/* See set print symbol on|off.  */
+      "unions",			/* See set print union on|off.  */
+      /* C++ options.  */
+      "deref_refs",		/* No corresponding setting.  */
+      "actual_objects",		/* See set print object on|off.  */
+      "static_members",		/* See set print static-members on|off.  */
+      /* C non-bool options.  */
+      "max_elements", 		/* See set print elements N.  */
+      "repeat_threshold",	/* See set print repeats.  */
+      "format",			/* The format passed to the print command.  */
+      NULL
+    };
+
+  /* This function has too many arguments to be useful as positionals, so
+     the user should specify them all as keyword arguments.
+     Python 3.3 and later have a way to specify it (both in C and Python
+     itself), but we could be compiled with older versions, so we just
+     check that the args tuple is empty.  */
+  Py_ssize_t positional_count = PyObject_Length (args);
+  if (positional_count < 0)
+    return NULL;
+  else if (positional_count > 0)
+    {
+      /* This matches the error message that Python 3.3 raises when
+	 passing positionals to functions expecting keyword-only
+	 arguments.  */
+      PyErr_Format (PyExc_TypeError,
+		    "format_string() takes 0 positional arguments but %zu were given",
+		    positional_count);
+      return NULL;
+    }
+
+  struct value_print_options opts;
+  get_user_print_options (&opts);
+  opts.deref_ref = 0;
+
+  /* We need objects for booleans as the "p" flag for bools is new in
+     Python 3.3.  */
+  PyObject *raw_obj = NULL;
+  PyObject *pretty_arrays_obj = NULL;
+  PyObject *pretty_structs_obj = NULL;
+  PyObject *array_indexes_obj = NULL;
+  PyObject *symbols_obj = NULL;
+  PyObject *unions_obj = NULL;
+  PyObject *deref_refs_obj = NULL;
+  PyObject *actual_objects_obj = NULL;
+  PyObject *static_members_obj = NULL;
+  char *format = NULL;
+  if (!gdb_PyArg_ParseTupleAndKeywords (args,
+					kw,
+					"|O!O!O!O!O!O!O!O!O!IIs",
+					keywords,
+					&PyBool_Type, &raw_obj,
+					&PyBool_Type, &pretty_arrays_obj,
+					&PyBool_Type, &pretty_structs_obj,
+					&PyBool_Type, &array_indexes_obj,
+					&PyBool_Type, &symbols_obj,
+					&PyBool_Type, &unions_obj,
+					&PyBool_Type, &deref_refs_obj,
+					&PyBool_Type, &actual_objects_obj,
+					&PyBool_Type, &static_members_obj,
+					&opts.print_max,
+					&opts.repeat_count_threshold,
+					&format))
+    return NULL;
+
+  /* Set boolean arguments.  */
+  if (!copy_py_bool_obj (&opts.raw, raw_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_arrays, pretty_arrays_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.prettyformat_structs, pretty_structs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.print_array_indexes, array_indexes_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.symbol_print, symbols_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.unionprint, unions_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.deref_ref, deref_refs_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.objectprint, actual_objects_obj))
+    return NULL;
+  if (!copy_py_bool_obj (&opts.static_field_print, static_members_obj))
+    return NULL;
+
+  /* Numeric arguments for which 0 means unlimited (which we represent as
+     UINT_MAX).  */
+  if (opts.print_max == 0)
+    opts.print_max = UINT_MAX;
+  if (opts.repeat_count_threshold == 0)
+    opts.repeat_count_threshold = UINT_MAX;
+
+  /* Other arguments.  */
+  if (format != NULL)
+    {
+      if (strlen (format) == 1)
+	opts.format = format[0];
+      else
+	{
+	  /* Mimic the message on standard Python ones for similar
+	     errors.  */
+	  PyErr_SetString (PyExc_ValueError,
+			   "a single character is required");
+	  return NULL;
+	}
+    }
+
+  string_file stb;
+
+  TRY
+    {
+      common_val_print (((value_object *) self)->value, &stb, 0,
+			&opts, python_language);
+    }
+  CATCH (except, RETURN_MASK_ALL)
+    {
+      GDB_PY_HANDLE_EXCEPTION (except);
+    }
+  END_CATCH
+
+  return PyUnicode_Decode (stb.c_str (), stb.size (), host_charset (), NULL);
+}
+
 /* A helper function that implements the various cast operators.  */
 
 static PyObject *
@@ -1944,6 +2103,11 @@ Return a lazy string representation of the value." },
 Return Unicode string representation of the value." },
   { "fetch_lazy", valpy_fetch_lazy, METH_NOARGS,
     "Fetches the value from the inferior, if it was lazy." },
+  { "format_string", (PyCFunction) valpy_format_string,
+    METH_VARARGS | METH_KEYWORDS,
+    "format_string (...) -> string\n\
+Return a string representation of the value using the specified\n\
+formatting options" },
   {NULL}  /* Sentinel */
 };
 
diff --git a/gdb/testsuite/gdb.python/py-format-string.c b/gdb/testsuite/gdb.python/py-format-string.c
new file mode 100644
index 0000000000..120ecce989
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.c
@@ -0,0 +1,118 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2019 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/>.  */
+
+typedef struct point
+{
+  int x;
+  int y;
+} point_t;
+
+typedef union
+{
+  int an_int;
+  char a_char;
+} union_t;
+
+typedef struct
+{
+  union_t the_union;
+} struct_union_t;
+
+typedef enum
+{
+  ENUM_FOO,
+  ENUM_BAR,
+} enum_t;
+
+typedef void (*function_t) (int);
+
+static void
+my_function(int n)
+{
+}
+
+#ifdef __cplusplus
+
+struct Base
+{
+  Base (int a_) : a (a_) {}
+
+  virtual int get_number () { return a; }
+
+  int a;
+
+  static int a_static_member;
+};
+
+int Base::a_static_member = 2019;
+
+struct Deriv : Base
+{
+  Deriv (int b_) : Base (42), b (b_) {}
+
+  virtual int get_number () { return b; }
+
+  int b;
+};
+
+#endif
+
+int global_symbol = 42;
+
+int
+main ()
+{
+  point_t a_point_t = { 42, 12 };
+  point_t *a_point_t_pointer = &a_point_t;
+#ifdef __cplusplus
+  point_t &a_point_t_ref = a_point_t;
+#endif
+  struct point another_point = { 123, 456 };
+
+  struct_union_t a_struct_with_union;
+  a_struct_with_union.the_union.an_int = 42;
+
+  enum_t an_enum = ENUM_BAR;
+
+  const char *a_string = "hello world";
+  const char *a_binary_string = "hello\0world";
+  const char a_binary_string_array[] = "hello\0world";
+
+  const int letters_repeat = 10;
+  char a_big_string[26 * letters_repeat + 1];
+  a_big_string[26 * letters_repeat] = '\0';
+  for (int i = 0; i < letters_repeat; i++)
+    for (char c = 'A'; c <= 'Z'; c++)
+      a_big_string[i * 26 + c - 'A'] = c;
+
+  int an_array[] = { 2, 3, 5 };
+
+  int an_array_with_repetition[] = {
+    1,					/*  1 time.   */
+    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,	/* 12 times.  */
+    5, 5, 5,				/*  3 times   */
+    };
+
+  int *a_symbol_pointer = &global_symbol;
+
+#ifdef __cplusplus
+  Deriv a_deriv (123);
+  Base &a_base_ref = a_deriv;
+#endif
+
+  return 0; /* break here */
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
new file mode 100644
index 0000000000..2f574fbfa5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -0,0 +1,957 @@
+# Copyright (C) 2009-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests the
+# gdb.Value.format_string () method.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+if [get_compiler_info c++] {
+    return -1
+}
+
+# Build inferior to language specification.
+proc build_inferior {exefile lang} {
+  global srcdir subdir srcfile testfile hex
+
+  if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${exefile}" executable "debug $lang"] != "" } {
+      untested "failed to compile in $lang mode"
+      return -1
+  }
+
+  return 0
+}
+
+# Restart GDB.
+proc prepare_gdb {exefile} {
+  global srcdir subdir srcfile testfile hex
+
+  gdb_exit
+  gdb_start
+  gdb_reinitialize_dir $srcdir/$subdir
+  gdb_load ${exefile}
+
+  # Skip all tests if Python scripting is not enabled.
+  if { [skip_python_tests] } { continue }
+
+  if ![runto_main] then {
+      perror "couldn't run to breakpoint"
+      return
+  }
+
+  # Load the pretty printer.
+  set remote_python_file \
+    [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+  gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+  runto_bp "break here"
+}
+
+# Set breakpoint and run to that breakpoint.
+proc runto_bp {bp} {
+  gdb_breakpoint [gdb_get_line_number $bp]
+  gdb_continue_to_breakpoint $bp
+}
+
+# Set an option using the GDB command in $set_cmd, execute $body, and then
+# restore the option using the GDB command in $unset_cmd.
+proc with_temp_option { set_cmd unset_cmd body } {
+  with_test_prefix $set_cmd {
+    gdb_test "$set_cmd" ".*"
+    uplevel 1 $body
+    gdb_test "$unset_cmd" ".*"
+  }
+}
+
+# A regular expression for a pointer.
+set default_pointer_regexp "0x\[a-fA-F0-9\]+"
+
+# A regular expression for a non-expanded C++ reference.
+#
+# Stringifying a C++ reference produces an address preceeded by a "@" in
+# Python, but, by default, the C++ reference/class is expanded by the
+# GDB print command.
+set default_ref_regexp "@${default_pointer_regexp}"
+
+# The whole content of the C variable a_big_string, i.e. the whole English
+# alphabet repeated 10 times.
+set whole_big_string ""
+set alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+for {set i 0} {$i < 10} {incr i} {
+  append whole_big_string $alphabet
+}
+unset alphabet
+
+# Produces a potentially cut down version of $whole_big_string like GDB
+# would represent it.
+# $max is the maximum number of characters allowed in the string (but
+# the return value may contain more to accound for the extra quotes and
+# "..." added by GDB).
+proc get_cut_big_string { max } {
+  global whole_big_string
+
+  set whole_size [string length $whole_big_string]
+  if { $max > $whole_size } {
+    return "\"${whole_big_string}\""
+  }
+
+  set cut_string [string range $whole_big_string 0 [expr $max - 1]]
+  return "\"${cut_string}\"..."
+}
+
+# A dictionary mapping from C variable names to their default string
+# representation when using str () or gdb.Value.format_string () with
+# no arguments.
+# This usually matches what the print command prints if used with no
+# options, except for C++ references which are not expanded by
+# default in Python.  See the comment above $default_ref_regexp.
+set default_regexp_dict [dict create \
+  "a_point_t"			"Pretty Point \\(42, 12\\)" \
+  "a_point_t_pointer"		$default_pointer_regexp \
+  "a_point_t_ref"		"Pretty Point \\(42, 12\\)" \
+  "another_point"		"Pretty Point \\(123, 456\\)" \
+  "a_struct_with_union"		"\\{the_union = \\{an_int = 42, a_char = 42 '\\*'\\}\\}" \
+  "an_enum"			"ENUM_BAR" \
+  "a_string"			"${default_pointer_regexp} \"hello world\"" \
+  "a_binary_string"		"${default_pointer_regexp} \"hello\"" \
+  "a_binary_string_array"	"\"hello\\\\000world\"" \
+  "a_big_string"		[get_cut_big_string 200] \
+  "an_array"			"\\{2, 3, 5\\}" \
+  "an_array_with_repetition"	"\\{1, 3 <repeats 12 times>, 5, 5, 5\\}" \
+  "a_symbol_pointer"		"${default_pointer_regexp} <global_symbol>" \
+  "a_base_ref"			"${default_ref_regexp}" \
+  ]
+
+# A sentinel value to pass to function to get them to use a default value
+# instead.
+# Note that we cannot use $undefined for default arguments in function
+# definitions as we would just get the literal "$undefined" string, so
+# we need to repeat the string.
+set undefined "\000UNDEFINED\000"
+
+# Return $value if it's not $undefined, otherwise return the default value
+# (from $default_regexp_dict) for the variable $var.
+proc get_value_or_default { var value } {
+  global undefined
+  if { $value != $undefined } {
+    return $value
+  }
+
+  global default_regexp_dict
+  return [dict get $default_regexp_dict $var]
+}
+
+# Check that using gdb.Value.format_string on the value representing the
+# variable $var produces $expected.
+proc check_format_string {
+	var
+	opts
+	{ expected "\000UNDEFINED\000" }
+	{ name "\000UNDEFINED\000" }
+  } {
+  global undefined
+
+  set expected [get_value_or_default $var $expected]
+  if { $name == $undefined } {
+    set name "${var} with option ${opts}"
+  }
+
+  gdb_test \
+    "python print (gdb.parse_and_eval ('${var}').format_string (${opts}))" \
+    $expected \
+    $name
+}
+
+# Check that printing $var with no options set, produces the expected
+# output.
+proc check_var_with_no_opts {
+	var
+	{ expected "\000UNDEFINED\000" }
+  } {
+  set expected [get_value_or_default $var $expected]
+
+  with_test_prefix "${var}" {
+    check_format_string \
+      $var \
+      "" \
+      $expected \
+      "no opts"
+    # str () should behave like gdb.Value.format_string () with no args.
+    gdb_test \
+      "python print (str (gdb.parse_and_eval ('${var}')))" \
+      $expected \
+      "str"
+  }
+}
+
+# Check that printing $var with $opt set to True and set to False,
+# produces the expected output.
+proc check_var_with_bool_opt {
+	opt
+	var
+	{ true_expected  "\000UNDEFINED\000" }
+	{ false_expected "\000UNDEFINED\000" }
+  } {
+  set true_expected  [get_value_or_default $var $true_expected]
+  set false_expected [get_value_or_default $var $false_expected]
+
+  with_test_prefix "${var} with option ${opt}" {
+    # Option set to True.
+    check_format_string \
+      $var \
+      "${opt}=True" \
+      $true_expected \
+      "${opt}=true"
+    # Option set to False.
+    check_format_string \
+      $var \
+      "${opt}=False" \
+      $false_expected \
+      "${opt}=false"
+  }
+}
+
+# Test gdb.Value.format_string with no options.
+proc test_no_opts {} {
+  global current_lang
+
+  check_var_with_no_opts "a_point_t"
+  check_var_with_no_opts "a_point_t_pointer"
+  check_var_with_no_opts "another_point"
+  check_var_with_no_opts "a_struct_with_union"
+  check_var_with_no_opts "an_enum"
+  check_var_with_no_opts "a_string"
+  check_var_with_no_opts "a_binary_string"
+  check_var_with_no_opts "a_binary_string_array"
+  check_var_with_no_opts "a_big_string"
+  check_var_with_no_opts "an_array"
+  check_var_with_no_opts "an_array_with_repetition"
+  check_var_with_no_opts "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_no_opts "a_point_t_ref"
+    check_var_with_no_opts "a_base_ref"
+  }
+}
+
+# Test the raw option for gdb.Value.format_string.
+proc test_raw {} {
+  global current_lang
+  global default_ref_regexp
+
+  check_var_with_bool_opt "raw" "a_point_t" \
+    "{x = 42, y = 12}"
+  check_var_with_bool_opt "raw" "a_point_t_pointer"
+  check_var_with_bool_opt "raw" "another_point" \
+    "{x = 123, y = 456}"
+  check_var_with_bool_opt "raw" "a_struct_with_union"
+  check_var_with_bool_opt "raw" "an_enum"
+  check_var_with_bool_opt "raw" "a_string"
+  check_var_with_bool_opt "raw" "a_binary_string"
+  check_var_with_bool_opt "raw" "a_binary_string_array"
+  check_var_with_bool_opt "raw" "a_big_string"
+  check_var_with_bool_opt "raw" "an_array"
+  check_var_with_bool_opt "raw" "an_array_with_repetition"
+  check_var_with_bool_opt "raw" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "raw" "a_point_t_ref" \
+      ${default_ref_regexp}
+    check_var_with_bool_opt "raw" "a_base_ref"
+  }
+
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "raw" "a_point_t" \
+      "{x = 42, y = 12}" \
+      "{x = 42, y = 12}"
+  }
+}
+
+# Test the pretty_arrays option for gdb.Value.format_string.
+proc test_pretty_arrays {} {
+  global current_lang
+
+  set an_array_pretty "  \\{2,\[\r\n\]+  3,\[\r\n\]+  5\\}"
+  set an_array_with_repetition_pretty \
+    "  \\{1,\[\r\n\]+  3 <repeats 12 times>,\[\r\n\]+  5,\[\r\n\]+  5,\[\r\n\]+  5\\}"
+
+  check_var_with_bool_opt "pretty_arrays" "a_point_t"
+  check_var_with_bool_opt "pretty_arrays" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_arrays" "another_point"
+  check_var_with_bool_opt "pretty_arrays" "a_struct_with_union"
+  check_var_with_bool_opt "pretty_arrays" "an_enum"
+  check_var_with_bool_opt "pretty_arrays" "a_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string"
+  check_var_with_bool_opt "pretty_arrays" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_arrays" "a_big_string"
+  check_var_with_bool_opt "pretty_arrays" "an_array" \
+    $an_array_pretty
+  check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+    $an_array_with_repetition_pretty
+  check_var_with_bool_opt "pretty_arrays" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_arrays" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_arrays" "a_base_ref"
+  }
+
+  with_temp_option "set print array on" "set print array off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array" \
+      $an_array_pretty
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+    check_var_with_bool_opt "pretty_arrays" "an_array_with_repetition" \
+      $an_array_with_repetition_pretty
+  }
+}
+
+# Test the pretty_structs option for gdb.Value.format_string.
+proc test_pretty_structs {} {
+  global current_lang
+
+  set a_struct_with_union_pretty \
+    "\\{\[\r\n\]+  the_union = \\{\[\r\n\]+    an_int = 42,\[\r\n\]+    a_char = 42 '\\*'\[\r\n\]+  \\}\[\r\n\]+\\}"
+
+  check_var_with_bool_opt "pretty_structs" "a_point_t"
+  check_var_with_bool_opt "pretty_structs" "a_point_t_pointer"
+  check_var_with_bool_opt "pretty_structs" "another_point"
+  check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+    $a_struct_with_union_pretty
+  check_var_with_bool_opt "pretty_structs" "an_enum"
+  check_var_with_bool_opt "pretty_structs" "a_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string"
+  check_var_with_bool_opt "pretty_structs" "a_binary_string_array"
+  check_var_with_bool_opt "pretty_structs" "a_big_string"
+  check_var_with_bool_opt "pretty_structs" "an_array"
+  check_var_with_bool_opt "pretty_structs" "an_array_with_repetition"
+  check_var_with_bool_opt "pretty_structs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "pretty_structs" "a_point_t_ref"
+    check_var_with_bool_opt "pretty_structs" "a_base_ref"
+  }
+
+  with_temp_option "set print structs on" "set print structs off" {
+    check_var_with_no_opts "a_struct_with_union"
+    check_var_with_bool_opt "pretty_structs" "a_struct_with_union" \
+      $a_struct_with_union_pretty
+  }
+
+  # point_t is usually printed through the pretty printer.
+  # Try disabling it.
+  with_temp_option \
+	"disable pretty-printer '' test_lookup_function" \
+	"enable pretty-printer '' test_lookup_function" {
+    check_var_with_no_opts "a_point_t" \
+      "{x = 42, y = 12}"
+    check_var_with_bool_opt "pretty_structs" "a_point_t" \
+      "\\{\[\r\n\]+  x = 42, *\[\r\n\]+  y = 12\[\r\n\]+\\}" \
+      "{x = 42, y = 12}" \
+  }
+}
+
+# Test the array_indexes option for gdb.Value.format_string.
+proc test_array_indexes {} {
+  global current_lang
+
+  set an_array_with_indexes "\\{\\\[0\\\] = 2, \\\[1\\\] = 3, \\\[2\\\] = 5\\}"
+  set an_array_with_repetition_with_indexes \
+    "\\{\\\[0\\\] = 1, \\\[1\\\] = 3 <repeats 12 times>, \\\[13\\\] = 5, \\\[14\\\] = 5, \\\[15\\\] = 5\\}"
+
+  check_var_with_bool_opt "array_indexes" "a_point_t"
+  check_var_with_bool_opt "array_indexes" "a_point_t_pointer"
+  check_var_with_bool_opt "array_indexes" "another_point"
+  check_var_with_bool_opt "array_indexes" "a_struct_with_union"
+  check_var_with_bool_opt "array_indexes" "an_enum"
+  check_var_with_bool_opt "array_indexes" "a_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string"
+  check_var_with_bool_opt "array_indexes" "a_binary_string_array"
+  check_var_with_bool_opt "array_indexes" "a_big_string"
+  check_var_with_bool_opt "array_indexes" "an_array" \
+    $an_array_with_indexes
+  check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+    $an_array_with_repetition_with_indexes
+  check_var_with_bool_opt "array_indexes" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "array_indexes" "a_point_t_ref"
+    check_var_with_bool_opt "array_indexes" "a_base_ref"
+  }
+
+  with_temp_option \
+	"set print array-indexes on" \
+	"set print array-indexes off" {
+    check_var_with_no_opts "an_array" \
+      $an_array_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array" \
+      $an_array_with_indexes
+
+    check_var_with_no_opts "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+    check_var_with_bool_opt "array_indexes" "an_array_with_repetition" \
+      $an_array_with_repetition_with_indexes
+  }
+}
+
+# Test the symbols option for gdb.Value.format_string.
+proc test_symbols {} {
+  global undefined
+  global current_lang
+  global default_pointer_regexp
+
+  check_var_with_bool_opt "symbols" "a_point_t"
+  check_var_with_bool_opt "symbols" "a_point_t_pointer"
+  check_var_with_bool_opt "symbols" "another_point"
+  check_var_with_bool_opt "symbols" "a_struct_with_union"
+  check_var_with_bool_opt "symbols" "an_enum"
+  check_var_with_bool_opt "symbols" "a_string"
+  check_var_with_bool_opt "symbols" "a_binary_string"
+  check_var_with_bool_opt "symbols" "a_binary_string_array"
+  check_var_with_bool_opt "symbols" "a_big_string"
+  check_var_with_bool_opt "symbols" "an_array"
+  check_var_with_bool_opt "symbols" "an_array_with_repetition"
+  check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+    $undefined \
+    $default_pointer_regexp
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "symbols" "a_point_t_ref"
+    check_var_with_bool_opt "symbols" "a_base_ref"
+  }
+
+  with_temp_option "set print symbol off" "set print symbol on" {
+    check_var_with_no_opts "a_symbol_pointer" \
+      $default_pointer_regexp
+    check_var_with_bool_opt "symbols" "a_symbol_pointer" \
+      $undefined \
+      $default_pointer_regexp
+  }
+}
+
+# Test the unions option for gdb.Value.format_string.
+proc test_unions {} {
+  global undefined
+  global current_lang
+
+  check_var_with_bool_opt "unions" "a_point_t"
+  check_var_with_bool_opt "unions" "a_point_t_pointer"
+  check_var_with_bool_opt "unions" "another_point"
+  check_var_with_bool_opt "unions" "a_struct_with_union" \
+    $undefined \
+    "\\{the_union = \\{...\\}\\}"
+  check_var_with_bool_opt "unions" "an_enum"
+  check_var_with_bool_opt "unions" "a_string"
+  check_var_with_bool_opt "unions" "a_binary_string"
+  check_var_with_bool_opt "unions" "a_binary_string_array"
+  check_var_with_bool_opt "unions" "a_big_string"
+  check_var_with_bool_opt "unions" "an_array"
+  check_var_with_bool_opt "unions" "an_array_with_repetition"
+  check_var_with_bool_opt "unions" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "unions" "a_point_t_ref"
+    check_var_with_bool_opt "unions" "a_base_ref"
+  }
+
+  with_temp_option "set print union off" "set print union on" {
+    check_var_with_no_opts "a_struct_with_union" \
+      "\\{the_union = \\{...\\}\\}"
+    check_var_with_bool_opt "unions" "a_struct_with_union" \
+      $undefined \
+      "\\{the_union = \\{...\\}\\}"
+  }
+}
+
+# Test the deref_refs option for gdb.Value.format_string.
+proc test_deref_refs {} {
+  global current_lang
+  global default_pointer_regexp
+  global default_ref_regexp
+
+  check_var_with_bool_opt "deref_refs" "a_point_t"
+  check_var_with_bool_opt "deref_refs" "a_point_t_pointer"
+  check_var_with_bool_opt "deref_refs" "another_point"
+  check_var_with_bool_opt "deref_refs" "a_struct_with_union"
+  check_var_with_bool_opt "deref_refs" "an_enum"
+  check_var_with_bool_opt "deref_refs" "a_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string"
+  check_var_with_bool_opt "deref_refs" "a_binary_string_array"
+  check_var_with_bool_opt "deref_refs" "a_big_string"
+  check_var_with_bool_opt "deref_refs" "an_array"
+  check_var_with_bool_opt "deref_refs" "an_array_with_repetition"
+  check_var_with_bool_opt "deref_refs" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    check_var_with_bool_opt "deref_refs" "a_point_t_ref"
+    check_var_with_bool_opt "deref_refs" "a_base_ref" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42, static a_static_member = 2019\\}"
+  }
+}
+
+# Test the actual_objects option for gdb.Value.format_string.
+proc test_actual_objects {} {
+  global current_lang
+
+  check_var_with_bool_opt "actual_objects" "a_point_t"
+  check_var_with_bool_opt "actual_objects" "a_point_t_pointer"
+  check_var_with_bool_opt "actual_objects" "another_point"
+  check_var_with_bool_opt "actual_objects" "a_struct_with_union"
+  check_var_with_bool_opt "actual_objects" "an_enum"
+  check_var_with_bool_opt "actual_objects" "a_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string"
+  check_var_with_bool_opt "actual_objects" "a_binary_string_array"
+  check_var_with_bool_opt "actual_objects" "a_big_string"
+  check_var_with_bool_opt "actual_objects" "an_array"
+  check_var_with_bool_opt "actual_objects" "an_array_with_repetition"
+  check_var_with_bool_opt "actual_objects" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+    check_var_with_bool_opt "actual_objects" "a_base_ref"
+
+    with_temp_option "set print object on" "set print object off" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "actual_objects" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "actual_objects" "a_base_ref"
+    }
+  }
+}
+
+# Test the static_members option for gdb.Value.format_string.
+proc test_static_members {} {
+  global current_lang
+
+  check_var_with_bool_opt "static_members" "a_point_t"
+  check_var_with_bool_opt "static_members" "a_point_t_pointer"
+  check_var_with_bool_opt "static_members" "another_point"
+  check_var_with_bool_opt "static_members" "a_struct_with_union"
+  check_var_with_bool_opt "static_members" "an_enum"
+  check_var_with_bool_opt "static_members" "a_string"
+  check_var_with_bool_opt "static_members" "a_binary_string"
+  check_var_with_bool_opt "static_members" "a_binary_string_array"
+  check_var_with_bool_opt "static_members" "a_big_string"
+  check_var_with_bool_opt "static_members" "an_array"
+  check_var_with_bool_opt "static_members" "an_array_with_repetition"
+  check_var_with_bool_opt "static_members" "a_symbol_pointer"
+
+  if { $current_lang == "c++" } {
+    # Nothing changes in all of the C++ tests because deref_refs is not
+    # True.
+    check_var_with_bool_opt "static_members" "a_point_t_ref"
+    check_var_with_bool_opt "static_members" "a_base_ref"
+
+    with_temp_option \
+	"set print static-members off" \
+	"set print static-members on" {
+      check_var_with_no_opts "a_point_t_ref"
+      check_var_with_bool_opt "static_members" "a_point_t_ref"
+
+      check_var_with_no_opts "a_base_ref"
+      check_var_with_bool_opt "static_members" "a_base_ref"
+    }
+  }
+}
+
+# Test the max_elements option for gdb.Value.format_string.
+proc test_max_elements {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 200 is the default maximum number of elements, so setting it should
+  # not change the output.
+  set opts "max_elements=200"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "max_elements=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"hel\"..."
+    # This will print four characters instead of three, see
+    # <https://sourceware.org/bugzilla/show_bug.cgi?id=24331>.
+    check_format_string "a_binary_string_array" $opts \
+      "\"hell\"..."
+    check_format_string "a_big_string" $opts \
+      [get_cut_big_string 3]
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>...\\}"
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 1,000 (we don't have that many elements) and 0 (unlimited) should
+  # mean no truncation.
+  foreach opts { "max_elements=1000" "max_elements=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts \
+        [get_cut_big_string 1000]
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print elements 4" "set print elements 200" {
+    check_format_string "a_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string" "" \
+      "${default_pointer_regexp} \"hell\"..."
+    check_format_string "a_binary_string_array" "" \
+      "\"hell\"..."
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>...\\}"
+  }
+}
+
+# Test the repeat_threshold option for gdb.Value.format_string.
+proc test_repeat_threshold {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # 10 is the default threshold for repeated items, so setting it should
+  # not change the output.
+  set opts "repeat_threshold=10"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=1"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o world\""
+    check_format_string "a_binary_string" $opts \
+      "${default_pointer_regexp} \"he\", 'l' <repeats 2 times>, \"o\""
+    check_format_string "a_binary_string_array" $opts \
+      "\"he\", 'l' <repeats 2 times>, \"o\\\\000world\""
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  set opts "repeat_threshold=3"
+  with_test_prefix $opts {
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts
+    check_format_string "an_enum" $opts
+    check_format_string "a_string" $opts
+    check_format_string "a_binary_string" $opts
+    check_format_string "a_binary_string_array" $opts
+    check_format_string "a_big_string" $opts
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Both 100 (we don't have that many repeated elements) and 0 (unlimited)
+  # should mean no truncation.
+  foreach opts { "repeat_threshold=100" "repeat_threshold=0" } {
+    with_test_prefix $opts {
+      check_format_string "a_point_t" $opts
+      check_format_string "a_point_t_pointer" $opts
+      check_format_string "another_point" $opts
+      check_format_string "a_struct_with_union" $opts
+      check_format_string "an_enum" $opts
+      check_format_string "a_string" $opts
+      check_format_string "a_binary_string" $opts
+      check_format_string "a_binary_string_array" $opts
+      check_format_string "a_big_string" $opts
+      check_format_string "an_array" $opts
+      check_format_string "an_array_with_repetition" $opts \
+        "\\{1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5\\}"
+      check_format_string "a_symbol_pointer" $opts
+
+      if { $current_lang == "c++" } {
+        check_format_string "a_point_t_ref" $opts
+        check_format_string "a_base_ref" $opts
+      }
+    }
+  }
+
+  with_temp_option "set print repeats 1" "set print repeats 10" {
+    check_format_string "an_array_with_repetition" "" \
+      "\\{1, 3 <repeats 12 times>, 5 <repeats 3 times>\\}"
+  }
+}
+
+# Test the format option for gdb.Value.format_string.
+proc test_format {} {
+  global current_lang
+  global default_pointer_regexp
+
+  # Hexadecimal.
+  set opts "format='x'"
+  with_test_prefix $opts {
+    gdb_test "python print (gdb.Value (42).format_string (${opts}))" \
+      "0x2a" \
+      "42 with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 0x2a, a_char = 0x2a\\}\\}"
+    check_format_string "an_enum" $opts \
+      "0x1"
+    check_format_string "a_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $default_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x0, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{0x41, 0x42, 0x43, 0x44, 0x45, \[, x0-9a-f\]+\.\.\.\\}"
+    check_format_string "an_array" $opts \
+      "\\{0x2, 0x3, 0x5\\}"
+    check_format_string "an_array_with_repetition" $opts \
+      "\\{0x1, 0x3 <repeats 12 times>, 0x5, 0x5, 0x5\\}"
+    check_format_string "a_symbol_pointer" $opts \
+      $default_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+
+  # Decimal.
+  set opts "format='d'"
+  with_test_prefix $opts {
+    set decimal_pointer_regexp "\[0-9\]+"
+    gdb_test "python print (gdb.Value (0x2a).format_string (${opts}))" \
+      "42" \
+      "0x2a with option ${opts}"
+
+    check_format_string "a_point_t" $opts
+    check_format_string "a_point_t_pointer" $opts \
+      $decimal_pointer_regexp
+    check_format_string "another_point" $opts
+    check_format_string "a_struct_with_union" $opts \
+      "\\{the_union = \\{an_int = 42, a_char = 42\\}\\}"
+    check_format_string "an_enum" $opts \
+      "1"
+    check_format_string "a_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string" $opts \
+      $decimal_pointer_regexp
+    check_format_string "a_binary_string_array" $opts \
+      "\\{104, 101, 108, 108, 111, 0, 119, 111, 114, 108, 100, 0\\}"
+    check_format_string "a_big_string" $opts \
+      "\\{65, 66, 67, 68, 69, \[, 0-9\]+\.\.\.\\}"
+    check_format_string "an_array" $opts
+    check_format_string "an_array_with_repetition" $opts
+    check_format_string "a_symbol_pointer" $opts \
+      $decimal_pointer_regexp
+
+    if { $current_lang == "c++" } {
+      check_format_string "a_point_t_ref" $opts
+      check_format_string "a_base_ref" $opts
+    }
+  }
+}
+
+# Test mixing options.
+proc test_mixed {} {
+  global current_lang
+  global default_ref_regexp
+  global default_pointer_regexp
+
+  check_format_string "a_point_t" \
+    "raw=True, format='x'" \
+    "\\{x = 0x2a, y = 0xc\\}"
+
+  check_format_string "an_array" \
+    "array_indexes=True, pretty_arrays=True" \
+    "  \\{\\\[0\\\] = 2,\[\r\n\]+  \\\[1\\\] = 3,\[\r\n\]+  \\\[2\\\] = 5\\}"
+
+  check_format_string "a_struct_with_union" \
+    "pretty_structs=True, unions=False" \
+    "\\{\[\r\n\]+  the_union = \\{\.\.\.\\}\[\r\n\]+\\}"
+
+  check_format_string "a_symbol_pointer" \
+    "symbols=False, format='d'" \
+    "\[0-9\]+"
+
+  if { $current_lang == "c++" } {
+    check_format_string "a_point_t_ref" \
+      "deref_refs=True, actual_objects=True, raw=True" \
+      "${default_ref_regexp}: \\{x = 42, y = 12\\}"
+
+    check_format_string "a_base_ref" \
+      "deref_refs=True, static_members=False" \
+      "${default_ref_regexp}: \\{_vptr\\.Base = ${default_pointer_regexp} <vtable for Deriv\\+16>, a = 42\\}"
+  }
+}
+
+# Test passing invalid arguments to gdb.Value.format_string.
+proc test_invalid_args {} {
+  check_format_string \
+    "a_point_t" \
+    "12" \
+    "TypeError: format_string\\(\\) takes 0 positional arguments but 1 were given.*"
+
+  check_format_string \
+    "a_point_t" \
+    "invalid=True" \
+    "TypeError: 'invalid' is an invalid keyword argument for this function.*"
+
+  check_format_string \
+    "a_point_t" \
+    "raw='hello'" \
+    "TypeError: argument 1 must be bool, not str.*"
+
+  check_format_string \
+    "a_point_t" \
+    "format='xd'" \
+    "ValueError: a single character is required.*"
+}
+
+# Run all the tests in common for both C and C++.
+proc test_all_common {} {
+  # No options.
+  test_no_opts
+  # Single options set to True/False.
+  test_raw
+  test_pretty_arrays
+  test_pretty_structs
+  test_array_indexes
+  test_symbols
+  test_unions
+  test_deref_refs
+  test_actual_objects
+  test_static_members
+  test_max_elements
+  test_repeat_threshold
+  test_format
+  # Multiple options mixed together.
+  test_mixed
+  # Various error conditions.
+  test_invalid_args
+}
+
+# The current language ("c" or "c++" while running tests).
+set current_lang ""
+
+with_test_prefix "format_string" {
+  # Perform C Tests.
+  if { [build_inferior "${binfile}" "c"] == 0 } {
+    with_test_prefix "lang_c" {
+      set current_lang "c"
+      prepare_gdb "${binfile}"
+      test_all_common
+    }
+  }
+
+  # Perform C++ Tests.
+  if { [build_inferior "${binfile}-cxx" "c++"] == 0 } {
+    with_test_prefix "lang_cpp" {
+      set current_lang "c++"
+      prepare_gdb "${binfile}-cxx"
+      test_all_common
+    }
+  }
+}
diff --git a/gdb/testsuite/gdb.python/py-format-string.py b/gdb/testsuite/gdb.python/py-format-string.py
new file mode 100644
index 0000000000..c2ad88e862
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-format-string.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2008-2019 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/>.
+
+# This file is part of the GDB testsuite.  It tests python pretty
+# printers.
+
+import gdb
+
+class PointPrinter (object):
+    def __init__ (self, val):
+        self.val = val
+
+    def to_string (self):
+        return 'Pretty Point (%s, %s)' % (self.val['x'], self.val['y'])
+
+def test_lookup_function (val):
+    "Look-up and return a pretty-printer that can print val."
+
+    # Get the type.
+    type = val.type
+
+    # If it points to a reference, get the reference.
+    if type.code == gdb.TYPE_CODE_REF:
+        type = type.target ()
+
+    # Get the unqualified type, stripped of typedefs.
+    type = type.unqualified ().strip_typedefs ()
+
+    # Get the type name.
+    typename = type.tag
+
+    if typename == 'point':
+        return PointPrinter (val)
+
+    return None
+
+gdb.pretty_printers.append (test_lookup_function)
-- 
2.19.2


[-- Attachment #3: Type: text/plain, Size: 24 bytes --]




-- 
Marco Barisione


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v4] Add gdb.Value.format_string ()
  2019-03-28 13:55           ` [PATCH v4] " Marco Barisione
@ 2019-03-29 15:03             ` Tom Tromey
  2019-04-12 20:18             ` New failures on gdb.python/py-format-string.exp - unix/-m32 (was: Re: [PATCH v4] Add gdb.Value.format_string ()) Sergio Durigan Junior
  1 sibling, 0 replies; 16+ messages in thread
From: Tom Tromey @ 2019-03-29 15:03 UTC (permalink / raw)
  To: Marco Barisione; +Cc: Tom Tromey, Kevin Buettner, gdb-patches, Eli Zaretskii

Marco> I’ve attached a patch which is identical to the previous one except for:

Thanks.  Do you have a copyright assignment?  I see you aren't in
MAINTAINERS, so I presume you don't have write access.  Contact me off
list and we can set that up.

Marco> I’ve done this and indented the braces by two spaces and the array’s
Marco> content by another two spaces.
Marco> This seems to match my understanding of the GNU/GDB indentation style, but
Marco> I’m not 100% sure it’s the correct one as, in other places in the code
Marco> base, this kind of brace is not indented.

Yeah, I think that's the rule for braces, but it wasn't consistently
followed for array initializers in the past.

Tom

^ permalink raw reply	[flat|nested] 16+ messages in thread

* New failures on gdb.python/py-format-string.exp - unix/-m32 (was: Re: [PATCH v4] Add gdb.Value.format_string ())
  2019-03-28 13:55           ` [PATCH v4] " Marco Barisione
  2019-03-29 15:03             ` Tom Tromey
@ 2019-04-12 20:18             ` Sergio Durigan Junior
  1 sibling, 0 replies; 16+ messages in thread
From: Sergio Durigan Junior @ 2019-04-12 20:18 UTC (permalink / raw)
  To: Marco Barisione; +Cc: Tom Tromey, Kevin Buettner, gdb-patches, Eli Zaretskii

On Thursday, March 28 2019, Marco Barisione wrote:

> On 27 Mar 2019, at 20:16, Tom Tromey <tom@tromey.com> wrote:
>> I only have two nits with this patch.  The rest looks very good, thank
>> you for doing this.
>
> Thanks!
>
> I’ve attached a patch which is identical to the previous one except for:
> * Indentation fixed;
> * The NEWS file update was moved to the “Since GDB 8.3” section;
> * Test fixed to not expect a stray space between a comma and a newline
>   any more (as the original behaviour was fixed by Andrew Burgess a few
>   days ago).

Hi Marco,

The new test FAILs when tested on x86_64 with -m32.  I.e.,:

  make check-gdb TESTS=gdb.python/py-format-string.exp RUNTESTFLAGS='--target_board unix/-m32'

The failures I'm seeing:

  python print (gdb.parse_and_eval ('a_base_ref').format_string (deref_refs=True))
  @0xffffbb98: {_vptr.Base = 0x80487a4 <vtable for Deriv+8>, a = 42, static a_static_member = 2019}
  (gdb) FAIL: gdb.python/py-format-string.exp: format_string: lang_cpp: a_base_ref with option deref_refs: deref_refs=true
  ...
  python print (gdb.parse_and_eval ('a_base_ref').format_string (deref_refs=True, static_members=False))
  @0xffffbb98: {_vptr.Base = 0x80487a4 <vtable for Deriv+8>, a = 42}
  (gdb) FAIL: gdb.python/py-format-string.exp: format_string: lang_cpp: a_base_ref with option deref_refs=True, static_members=False

Here is the BuildBot run:

  https://gdb-build.sergiodj.net/builders/Fedora-x86_64-m32/builds/12191

And the logs:

  https://gdb-build.sergiodj.net/results/Fedora-x86_64-m32/52/52093e1b936fa4f3f8bb3868c5a44d0df25c8db4/

Let me know if you need help investigating these.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2019-04-12 20:18 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-16 11:18 [PATCH] Add gdb.Value.format_string () Marco Barisione
2019-03-17 20:50 ` Kevin Buettner
2019-03-17 23:41   ` Marco Barisione
2019-03-18  9:22   ` [PATCH v2] " Marco Barisione
2019-03-18 16:34     ` Kevin Buettner
2019-03-18 17:49       ` Eli Zaretskii
2019-03-18 18:17       ` [PATCH v3] " Marco Barisione
2019-03-27 20:16         ` Tom Tromey
2019-03-28 13:55           ` [PATCH v4] " Marco Barisione
2019-03-29 15:03             ` Tom Tromey
2019-04-12 20:18             ` New failures on gdb.python/py-format-string.exp - unix/-m32 (was: Re: [PATCH v4] Add gdb.Value.format_string ()) Sergio Durigan Junior
     [not found]       ` <D8FDF6CA-2755-4AC2-BE63-A76311F566E6@undo.io>
2019-03-18 20:00         ` [PATCH v3] Add gdb.Value.format_string () Eli Zaretskii
2019-03-18 21:49           ` [PATCH v4] " Marco Barisione
2019-03-19  6:48             ` Eli Zaretskii
2019-03-26 12:07             ` [PING][PATCH " Marco Barisione
2019-03-18 17:49     ` [PATCH v2] " Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).