[PATCH] Add gdb.Value.format_string ()

Marco Barisione
2019-03-16T11:18:44Z

[-- 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