public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
@ 2019-03-27 21:53   ` Andrew Burgess
  2019-03-28 12:43     ` Pedro Alves
  2019-03-27 21:53   ` [PATCH 2/2] gdb: Introduce 'print max-depth' feature Andrew Burgess
                     ` (6 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-03-27 21:53 UTC (permalink / raw)
  To: gdb-patches; +Cc: richard.bunt, Andrew Burgess

This commit is preparation work for the next commit, and by itself
makes no user visible change to GDB.  I've split this work into a
separate commit in order to make code review easier.

This commit adds a new field 'la_struct_too_deep_ellipsis' to the
language struct, this string will be used in the next commit to print
a language specific string from within the generic value printing
code.

In this commit I add the new field and initialise it for each
language, however at this stage the new field is never used.

gdb/ChangeLog:

	* language.h (struct language_defn) <la_struct_too_deep_ellipsis>:
	New field.
	* ada-lang.c (ada_language_defn): Initialise new field.
	* c-lang.c (c_language_defn): Likewise.
	(cplus_language_defn): Likewise.
	(asm_language_defn): Likewise.
	(minimal_language_defn): Likewise.
	* d-lang.c (d_language_defn): Likewise.
	* f-lang.c (f_language_defn): Likewise.
	* go-lang.c (go_language_defn): Likewise.
	* language.c (unknown_language_defn): Likewise.
	(auto_language_defn): Likewise.
	* m2-lang.c (m2_language_defn): Likewise.
	* objc-lang.c (objc_language_defn): Likewise.
	* opencl-lang.c (opencl_language_defn): Likewise.
	* p-lang.c (pascal_language_defn): Likewise.
	* rust-lang.c (rust_language_defn): Likewise.
---
 gdb/ChangeLog     | 20 ++++++++++++++++++++
 gdb/ada-lang.c    |  1 +
 gdb/c-lang.c      |  4 ++++
 gdb/d-lang.c      |  1 +
 gdb/f-lang.c      |  1 +
 gdb/go-lang.c     |  1 +
 gdb/language.c    |  2 ++
 gdb/language.h    |  5 +++++
 gdb/m2-lang.c     |  1 +
 gdb/objc-lang.c   |  1 +
 gdb/opencl-lang.c |  1 +
 gdb/p-lang.c      |  1 +
 gdb/rust-lang.c   |  1 +
 13 files changed, 40 insertions(+)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 692d52a9551..b7cd63ab5a7 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14380,6 +14380,7 @@ extern const struct language_defn ada_language_defn = {
   &ada_varobj_ops,
   NULL,
   NULL,
+  "(...)",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 36c750204da..cc414135be3 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -874,6 +874,7 @@ extern const struct language_defn c_language_defn =
   &c_varobj_ops,
   c_get_compile_context,
   c_compute_program,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
@@ -1019,6 +1020,7 @@ extern const struct language_defn cplus_language_defn =
   &cplus_varobj_ops,
   cplus_get_compile_context,
   cplus_compute_program,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
@@ -1073,6 +1075,7 @@ extern const struct language_defn asm_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
@@ -1127,5 +1130,6 @@ extern const struct language_defn minimal_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 637e012b32e..a5c8ce27941 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -251,6 +251,7 @@ extern const struct language_defn d_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 7bd119690b4..c894c105d16 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -378,6 +378,7 @@ extern const struct language_defn f_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "(...)",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index 5af88f805a7..9718c69c861 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -612,6 +612,7 @@ extern const struct language_defn go_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/language.c b/gdb/language.c
index ea294e670b6..69696d62f6e 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -878,6 +878,7 @@ const struct language_defn unknown_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
@@ -929,6 +930,7 @@ const struct language_defn auto_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/language.h b/gdb/language.h
index d56ec200208..ba875760f00 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -446,6 +446,11 @@ struct language_defn
 				       const struct block *expr_block,
 				       CORE_ADDR expr_pc);
 
+    /* This string is used by the 'set print max-depth' setting.  When GDB
+       replaces a struct or union (during value printing) that is "too
+       deep" this string is displayed instead.  */
+    const char *la_struct_too_deep_ellipsis;
+
     /* Add fields above this point, so the magic number is always last.  */
     /* Magic number for compat checking.  */
 
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 1a72aba73c9..004fd7022cb 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -399,6 +399,7 @@ extern const struct language_defn m2_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index ceef482ae35..2316f1e90e0 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -409,6 +409,7 @@ extern const struct language_defn objc_language_defn = {
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index 03699b14251..7ab95c9a94a 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1087,6 +1087,7 @@ extern const struct language_defn opencl_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
 
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index b61273f356b..c94ab5f21bb 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -460,5 +460,6 @@ extern const struct language_defn pascal_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "{...}",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 0c6c13d738e..926580ff96c 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2143,5 +2143,6 @@ extern const struct language_defn rust_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  "(...)",			/* la_struct_too_deep_ellipsis */
   LANG_MAGIC
 };
-- 
2.14.5

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

* [PATCH 0/2] Add new 'print max-depth' feature
@ 2019-03-27 21:53 ` Andrew Burgess
  2019-03-27 21:53   ` [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
                     ` (7 more replies)
  0 siblings, 8 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-03-27 21:53 UTC (permalink / raw)
  To: gdb-patches; +Cc: richard.bunt, Andrew Burgess

To make code review a little easier I split this feature into two
parts, first adding a new field to the language structure, which is
then used in the second commit.

For review purposes you're probably better looking at the second
commit first, hopefully afer that the motivation for the first commit
should be clearer.

--

Andrew Burgess (2):
  gdb: Introduce new language field la_struct_too_deep_ellipsis
  gdb: Introduce 'print max-depth' feature

 gdb/ChangeLog                               |  42 ++++++
 gdb/NEWS                                    |   9 ++
 gdb/ada-lang.c                              |   1 +
 gdb/c-lang.c                                |   4 +
 gdb/cp-valprint.c                           |  32 +++--
 gdb/d-lang.c                                |   1 +
 gdb/doc/ChangeLog                           |   7 +
 gdb/doc/gdb.texinfo                         |  57 ++++++++
 gdb/doc/guile.texi                          |   3 +
 gdb/doc/python.texi                         |   3 +
 gdb/f-lang.c                                |   1 +
 gdb/go-lang.c                               |   1 +
 gdb/guile/scm-pretty-print.c                |  23 ++-
 gdb/language.c                              |   2 +
 gdb/language.h                              |   5 +
 gdb/m2-lang.c                               |   1 +
 gdb/objc-lang.c                             |   1 +
 gdb/opencl-lang.c                           |   1 +
 gdb/p-lang.c                                |   1 +
 gdb/python/py-prettyprint.c                 |  22 ++-
 gdb/rust-lang.c                             |   1 +
 gdb/testsuite/ChangeLog                     |  12 ++
 gdb/testsuite/gdb.base/max-depth.c          | 209 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/max-depth.exp        | 122 ++++++++++++++++
 gdb/testsuite/gdb.fortran/max-depth.exp     |  41 ++++++
 gdb/testsuite/gdb.fortran/max-depth.f90     |  40 ++++++
 gdb/testsuite/gdb.python/py-nested-maps.c   | 122 ++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.exp | 211 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.py  | 129 +++++++++++++++++
 gdb/testsuite/lib/gdb.exp                   |  30 ++++
 gdb/valprint.c                              |  66 ++++++++-
 gdb/valprint.h                              |   3 +
 32 files changed, 1188 insertions(+), 15 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/max-depth.c
 create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py

-- 
2.14.5

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

* [PATCH 2/2] gdb: Introduce 'print max-depth' feature
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
  2019-03-27 21:53   ` [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
@ 2019-03-27 21:53   ` Andrew Burgess
  2019-03-28 12:42     ` Pedro Alves
  2019-03-28 16:08     ` Eli Zaretskii
  2019-03-28 12:47   ` [PATCH 0/2] Add new " Pedro Alves
                     ` (5 subsequent siblings)
  7 siblings, 2 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-03-27 21:53 UTC (permalink / raw)
  To: gdb-patches; +Cc: richard.bunt, Andrew Burgess

Introduce a new print setting max-depth which can be set with 'set
print max-depth DEPTH'.  The default value of DEPTH is 20, but this
can also be set to unlimited.

When GDB is printing a value containing nested structures GDB will
stop descending at depth DEPTH.  Here is a small example:

    typedef struct s1 { int a; } s1;
    typedef struct s2 { s1 b; } s2;
    typedef struct s3 { s2 c; } s3;
    typedef struct s4 { s3 d; } s4;

    s4 var = { { { { 3 } } } };

The following table shows how various depth settings effect printing
of 'var':

    | Depth Setting | Result of 'p var'              |
    |---------------+--------------------------------|
    |     Unlimited | $1 = {d = {c = {b = {a = 3}}}} |
    |             4 | $1 = {d = {c = {b = {a = 3}}}} |
    |             3 | $1 = {d = {c = {b = {...}}}}   |
    |             2 | $1 = {d = {c = {...}}}         |
    |             1 | $1 = {d = {...}}               |
    |             0 | $1 = {...}                     |

Only structures, unions, and arrays are replaced in this way, scalars
and strings are not replaced.

The replacement is counted from the level at which you print, not from
the top level of the structure.  So, consider the above example and
this GDB session:

    (gdb) set print max-depth 2
    (gdb) p var
    $1 = {d = {c = {...}}}
    (gdb) p var.d
    $2 = {c = {b = {...}}}
    (gdb) p var.d.c
    $3 = {b = {a = 3}}

Setting the max-depth to 2 doesn't prevent the user from exploring
deeper into 'var' by asking for specific sub-fields to be printed.

The motivation behind this feature is to try and give the user more
control over how much is printed when examining large, complex data
structures.

The default max-depth of 20 means that there is a change in GDB's
default behaviour.  Someone printing a data structure with 20 levels
of nesting will now see '{...}' instead of their data, they would need
to adjust the max depth, or call print again naming a specific field
in order to dig deeper into their data structure.  If this is
considered a problem then we could increase the default, or even make
the default unlimited.

This commit relies on the previous commit, which added a new field to
the language structure, this new field was a string that contained the
pattern that should be used when a structure/union/array is replaced
in the output, this allows languages to use a syntax that is more
appropriate, mostly this will be selecting the correct types of
bracket '(...)' or '{...}', both of which are currently in use.

gdb/ChangeLog:

	* cp-valprint.c (cp_print_value_fields): Allow an additional level
	of depth when printing anonymous structs or unions.
	* guile/scm-pretty-print.c (ppscm_print_children): Don't print the
	children if the max-depth is already exceeded.  When printing the
	key of a map, allow one extra level of depth.
	* python/py-prettyprint.c (print_children): Likewise.
	* valprint.c: Add 'c-lang.h' include.
	(PRINT_MAX_DEPTH_DEFAULT): Define.
	(user_print_options): Initialise max_depth field.
	(val_print_scalar_or_string_type_p): New function.
	(val_print): Check to see if the max depth has been reached.
	(show_print_max_depth): New function.
	(_initialize_valprint): Add 'print max-depth' option.
	* valprint.h (struct value_print_options) <max_depth>: New field.
	* NEWS: Document new feature.

gdb/doc/ChangeLog:

	* gdb.texinfo (Print Settings): Document 'print max-depth'.
	* guile.texi (Guile Pretty Printing API): Document that 'print
	max-depth' can effect the display of a values children.
	* python.texi (Pretty Printing API): Likewise.

gdb/testsuite/ChangeLog:

	* gdb.base/max-depth.c: New file.
	* gdb.base/max-depth.exp: New file.
	* gdb.python/py-nested-maps.c: New file.
	* gdb.python/py-nested-maps.exp: New file.
	* gdb.python/py-nested-maps.py: New file.
	* gdb.fortran/max-depth.exp: New file.
	* gdb.fortran/max-depth.f90: New file.
	* lib/gdb.exp (get_print_expr_at_depths): New proc.
---
 gdb/ChangeLog                               |  22 +++
 gdb/NEWS                                    |   9 ++
 gdb/cp-valprint.c                           |  32 +++--
 gdb/doc/ChangeLog                           |   7 +
 gdb/doc/gdb.texinfo                         |  57 ++++++++
 gdb/doc/guile.texi                          |   3 +
 gdb/doc/python.texi                         |   3 +
 gdb/guile/scm-pretty-print.c                |  23 ++-
 gdb/python/py-prettyprint.c                 |  22 ++-
 gdb/testsuite/ChangeLog                     |  12 ++
 gdb/testsuite/gdb.base/max-depth.c          | 209 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/max-depth.exp        | 122 ++++++++++++++++
 gdb/testsuite/gdb.fortran/max-depth.exp     |  41 ++++++
 gdb/testsuite/gdb.fortran/max-depth.f90     |  40 ++++++
 gdb/testsuite/gdb.python/py-nested-maps.c   | 122 ++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.exp | 211 ++++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.py  | 129 +++++++++++++++++
 gdb/testsuite/lib/gdb.exp                   |  30 ++++
 gdb/valprint.c                              |  66 ++++++++-
 gdb/valprint.h                              |   3 +
 20 files changed, 1148 insertions(+), 15 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/max-depth.c
 create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py

diff --git a/gdb/NEWS b/gdb/NEWS
index edcc9c951a0..6b17640547f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -10,6 +10,15 @@
 
 * Support for Pointer Authentication on AArch64 Linux.
 
+* New commands
+
+set print max-depth
+show print max-depth
+  Allows deeply nested structures to be simplified when printing by
+  replacing deeply nested parts (beyond the max-depth) with ellipses.
+  The default max-depth is 20, but this can be set to unlimited to get
+  the old behaviour back.
+
 *** Changes in GDB 8.3
 
 * GDB and GDBserver now support access to additional registers on
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index 443c3b06dac..71da2b19755 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -272,10 +272,23 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 				   current_language->la_language,
 				   DMGL_PARAMS | DMGL_ANSI);
 	  annotate_field_name_end ();
+
+	  /* We tweak various options in a few cases below.  */
+	  struct value_print_options options_copy = *options;
+	  struct value_print_options *opts = &options_copy;
+
 	  /* Do not print leading '=' in case of anonymous
 	     unions.  */
 	  if (strcmp (TYPE_FIELD_NAME (type, i), ""))
 	    fputs_filtered (" = ", stream);
+	  else
+	    {
+	      /* If this is an anonymous field then we want to consider it
+		 as though it is at its parents depth when it comes to the
+		 max print depth.  */
+	      if (opts->max_depth != -1 && opts->max_depth < INT_MAX)
+		++opts->max_depth;
+	    }
 	  annotate_field_value ();
 
 	  if (!field_is_static (&TYPE_FIELD (type, i))
@@ -299,14 +312,12 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		}
 	      else
 		{
-		  struct value_print_options opts = *options;
-
-		  opts.deref_ref = 0;
+		  opts->deref_ref = 0;
 
 		  v = value_field_bitfield (type, i, valaddr, offset, val);
 
-		  common_val_print (v, stream, recurse + 1, &opts,
-				    current_language);
+		  common_val_print (v, stream, recurse + 1,
+				    opts, current_language);
 		}
 	    }
 	  else
@@ -334,8 +345,7 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		  END_CATCH
 
 		  cp_print_static_field (TYPE_FIELD_TYPE (type, i),
-					 v, stream, recurse + 1,
-					 options);
+					 v, stream, recurse + 1, opts);
 		}
 	      else if (i == vptr_fieldno && type == vptr_basetype)
 		{
@@ -347,20 +357,18 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		      CORE_ADDR addr;
 		      
 		      addr = extract_typed_address (valaddr + i_offset, i_type);
-		      print_function_pointer_address (options,
+		      print_function_pointer_address (opts,
 						      get_type_arch (type),
 						      addr, stream);
 		    }
 		}
 	      else
 		{
-		  struct value_print_options opts = *options;
-
-		  opts.deref_ref = 0;
+		  opts->deref_ref = 0;
 		  val_print (TYPE_FIELD_TYPE (type, i),
 			     offset + TYPE_FIELD_BITPOS (type, i) / 8,
 			     address,
-			     stream, recurse + 1, val, &opts,
+			     stream, recurse + 1, val, opts,
 			     current_language);
 		}
 	    }
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4c1d5e8294f..94f820d3ff2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -10560,6 +10560,63 @@
 Display the current threshold for printing repeated identical
 elements.
 
+@item set print max-depth @var{depth}
+@item set print max-depth unlimited
+@cindex printing nested structures
+Set the threshold after which nested structures are replaced with
+ellipsis, this can make visualising deeply nested structures easier.
+
+For example, given this C code
+
+@smallexample
+typedef struct s1 @{ int a; @} s1;
+typedef struct s2 @{ s1 b; @} s2;
+typedef struct s3 @{ s2 c; @} s3;
+typedef struct s4 @{ s3 d; @} s4;
+
+s4 var = @{ @{ @{ @{ 3 @} @} @} @};
+@end smallexample
+
+@noindent
+and with @code{set print max-depth unlimited} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{c = @{b = @{a = 3@}@}@}@}
+@end smallexample
+
+@noindent
+with @code{set print max-depth 0} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{...@}
+@end smallexample
+
+@noindent
+with @code{set print max-depth 1} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{...@}@}
+@end smallexample
+
+@noindent
+with @code{set print max-depth 2} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{c = @{...@}@}@}
+@end smallexample
+
+The pattern used to replace nested structures varies based on
+language, for most languages @code{@{...@}} is used, but Fortran uses
+@code{(...)}.
+
+@item show print max-depth
+Display the current threshold after which nested structures are
+replaces with ellipsis.
+
 @item set print null-stop
 @cindex @sc{null} elements in arrays
 Cause @value{GDBN} to stop printing the characters of an array when the first
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 721338b050c..f4c29dc3df8 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -1469,6 +1469,9 @@
 
 If @var{children} is @code{#f}, @value{GDBN} will act
 as though the value has no children.
+
+Children may be hidden from display based on the value of @samp{set
+print max-depth} (@pxref{Print Settings}).
 @end table
 @end deffn
 
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 56c925d4dd2..26320539db5 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -1285,6 +1285,9 @@
 
 This method is optional.  If it does not exist, @value{GDBN} will act
 as though the value has no children.
+
+Children may be hidden from display based on the value of @samp{set
+print max-depth} (@pxref{Print Settings}).
 @end defun
 
 @defun pretty_printer.display_hint (self)
diff --git a/gdb/guile/scm-pretty-print.c b/gdb/guile/scm-pretty-print.c
index e5096944b68..219e0ef63ad 100644
--- a/gdb/guile/scm-pretty-print.c
+++ b/gdb/guile/scm-pretty-print.c
@@ -744,6 +744,16 @@ ppscm_print_children (SCM printer, enum display_hint hint,
       return;
     }
 
+  if (options->max_depth > -1
+      && recurse >= options->max_depth)
+    {
+      if (language->la_language == language_fortran)
+	fputs_filtered ("(...)", stream);
+      else
+	fputs_filtered ("{...}", stream);
+      return;
+    }
+
   /* If we are printing a map or an array, we want special formatting.  */
   is_map = hint == HINT_MAP;
   is_array = hint == HINT_ARRAY;
@@ -898,7 +908,18 @@ ppscm_print_children (SCM printer, enum display_hint hint,
 	      ppscm_print_exception_unless_memory_error (except_scm, stream);
 	      break;
 	    }
-	  common_val_print (value, stream, recurse + 1, options, language);
+	  else
+	    {
+	      /* When printing the key of a map we allow one additional
+		 level of depth.  This means the key will print before the
+		 value does.  */
+	      struct value_print_options opt = *options;
+	      if (is_map && i % 2 == 0
+		  && opt.max_depth != -1
+		  && opt.max_depth < INT_MAX)
+		++opt.max_depth;
+	      common_val_print (value, stream, recurse + 1, &opt, language);
+	    }
 	}
 
       if (is_map && i % 2 == 0)
diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index e64d1f88af8..504644143f3 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -361,6 +361,16 @@ print_children (PyObject *printer, const char *hint,
   if (! PyObject_HasAttr (printer, gdbpy_children_cst))
     return;
 
+  if (options->max_depth > -1
+      && recurse >= options->max_depth)
+    {
+      if (language->la_language == language_fortran)
+	fputs_filtered ("(...)", stream);
+      else
+	fputs_filtered ("{...}", stream);
+      return;
+    }
+
   /* If we are printing a map or an array, we want some special
      formatting.  */
   is_map = hint && ! strcmp (hint, "map");
@@ -518,7 +528,17 @@ print_children (PyObject *printer, const char *hint,
 	      error (_("Error while executing Python code."));
 	    }
 	  else
-	    common_val_print (value, stream, recurse + 1, options, language);
+	    {
+	      /* When printing the key of a map we allow one additional
+		 level of depth.  This means the key will print before the
+		 value does.  */
+	      struct value_print_options opt = *options;
+	      if (is_map && i % 2 == 0
+		  && opt.max_depth != -1
+		  && opt.max_depth < INT_MAX)
+		++opt.max_depth;
+	      common_val_print (value, stream, recurse + 1, &opt, language);
+	    }
 	}
 
       if (is_map && i % 2 == 0)
diff --git a/gdb/testsuite/gdb.base/max-depth.c b/gdb/testsuite/gdb.base/max-depth.c
new file mode 100644
index 00000000000..cf2484fa59b
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-depth.c
@@ -0,0 +1,209 @@
+/* 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/>.  */
+
+struct s1
+{
+  int x;
+  int y;
+} s1;
+
+struct s2
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+  };
+} s2;
+
+struct s3
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  };
+} s3;
+
+struct s4
+{
+  int x;
+  int y;
+  struct
+  {
+    int x;
+    int y;
+    struct
+    {
+      int x;
+      int y;
+    } l2;
+  } l1;
+} s4;
+
+struct s5
+{
+  union
+  {
+    int raw[3];
+    struct
+    {
+      int x;
+      int y;
+      int z;
+    };
+  };
+} s5;
+
+typedef struct
+{
+  union
+  {
+    int raw[3];
+    struct
+    {
+      int x;
+      int y;
+      int z;
+    };
+  };
+} s6_t;
+
+s6_t s6;
+
+struct s7
+{
+  struct
+  {
+    int x;
+    int y;
+  };
+  struct
+  {
+    int z;
+    int a;
+  };
+  struct
+  {
+    int b;
+    int c;
+  };
+} s7;
+
+struct s8
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  } d1;
+} s8;
+
+struct s9
+{
+  int x;
+  int y;
+  struct
+  {
+    int k;
+    int j;
+    struct
+    {
+      int z;
+      int a;
+      struct
+      {
+        int b;
+        int c;
+      };
+    } d1;
+  };
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  } d2;
+} s9;
+
+struct s10
+{
+  int x[10];
+  int y;
+  struct
+  {
+    int k[10];
+    int j;
+    struct
+    {
+      int z;
+      int a;
+      struct
+      {
+        int b[10];
+        int c;
+      };
+    } d1;
+  };
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b[10];
+      int c;
+    };
+  } d2;
+} s10;
+
+struct s11
+{
+  int x;
+  char s[10];
+  struct
+  {
+    int z;
+    int a;
+  };
+} s11;
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/max-depth.exp b/gdb/testsuite/gdb.base/max-depth.exp
new file mode 100644
index 00000000000..777f66fab4e
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-depth.exp
@@ -0,0 +1,122 @@
+# 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/>.
+
+# Tests GDB's handling of 'set print max-depth'.
+
+# Only test C++ if we are able.  Always use C.
+if { [skip_cplus_tests] || [get_compiler_info "c++"] } {
+    set lang {c}
+} else {
+    set lang {c c++}
+}
+
+foreach l $lang {
+    set dir "$l"
+    remote_exec host "rm -rf [standard_output_file ${dir}]"
+    remote_exec host "mkdir -p [standard_output_file ${dir}]"
+}
+
+proc compile_and_run_tests { lang } {
+    global testfile
+    global srcfile
+    global binfile
+    global subdir
+    global srcdir
+
+    standard_testfile .c
+    set dir "$lang"
+
+    # Create the additional flags
+    set flags "debug"
+    lappend flags $lang
+
+    set binfile [standard_output_file ${dir}/${testfile}]
+    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "${flags}"] != "" } {
+	unresolved "failed to compile"
+	return 0
+    }
+
+    # Start with a fresh gdb.
+    clean_restart ${binfile}
+
+    # Advance to main
+    if { ![runto_main] } then {
+	fail "can't run to main"
+	return 0
+    }
+
+    # The max-depth setting has no effect as the anonymous scopes are
+    # ignored and the members are aggregated into the parent scope.
+    gdb_print_expr_at_depths "s1" {"{...}" \
+				       "{x = 0, y = 0}"\
+				       "{x = 0, y = 0}"}
+
+    gdb_print_expr_at_depths "s2" {"{...}" \
+				       "{x = 0, y = 0, {z = 0, a = 0}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0}}"}
+
+    gdb_print_expr_at_depths "s3" {"{...}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Increasing max-depth unfurls more of the object.
+    gdb_print_expr_at_depths "s4" {"{...}" \
+				       "{x = 0, y = 0, l1 = {...}}" \
+				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {...}}}" \
+				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {x = 0, y = 0}}}"}
+
+    # Check handling of unions, in this case 'raw' is printed instead of
+    # just {...} as this is not useful.
+    gdb_print_expr_at_depths "s5" {"{...}" \
+				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
+				       "{{raw = \\\{0, 0, 0\\\}, {x = 0, y = 0, z = 0}}}"}
+
+    # Check handling of typedefs.
+    gdb_print_expr_at_depths "s6" {"{...}" \
+				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
+				       "{{raw = \\\{0, 0, 0\\\}, {x = 0, y = 0, z = 0}}}"}
+
+    # Multiple anonymous structures in parallel.
+    gdb_print_expr_at_depths "s7" {"{...}" \
+				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}" \
+				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}"}
+
+    # Flip flop between named and anonymous. Expected to unfurl to the
+    # first non-anonymous type.
+    gdb_print_expr_at_depths "s8" {"{...}" \
+				       "{x = 0, y = 0, d1 = {...}}" \
+				       "{x = 0, y = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Imbalanced tree, this will unfurl one size more than the other as
+    # one side has more anonymous levels.
+    gdb_print_expr_at_depths "s9" {"{...}" \
+				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {...}}, d2 = {...}}" \
+				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}, d2 = {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Arrays are treated as an extra level, while scalars are not.
+    gdb_print_expr_at_depths "s10" {"{...}" \
+					"{x = {...}, y = 0, {k = {...}, j = 0, d1 = {...}}, d2 = {...}}" \
+					"{x = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, y = 0, {k = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, j = 0, d1 = {z = 0, a = 0, {b = {...}, c = 0}}}, d2 = {z = 0, a = 0, {b = {...}, c = 0}}}" \
+					"{x = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, y = 0, {k = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, j = 0, d1 = {z = 0, a = 0, {b = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, c = 0}}}, d2 = {z = 0, a = 0, {b = \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\}, c = 0}}}"}
+
+    # Strings are treated as scalars.
+    gdb_print_expr_at_depths "s11" {"{...}" \
+					"{x = 0, s = \"\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\", {z = 0, a = 0}}"}
+}
+
+foreach_with_prefix l $lang {
+    compile_and_run_tests $l
+}
diff --git a/gdb/testsuite/gdb.fortran/max-depth.exp b/gdb/testsuite/gdb.fortran/max-depth.exp
new file mode 100644
index 00000000000..9f1f9fac76f
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/max-depth.exp
@@ -0,0 +1,41 @@
+# 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/>.
+
+# This file tests GDB's handling of 'set print max-depth' for nested
+# fortran types.
+
+load_lib "fortran.exp"
+
+if { [skip_fortran_tests] } { continue }
+
+standard_testfile .f90
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug f90}] } {
+    return -1
+}
+
+if { ![runto MAIN__] } {
+    perror "Could not run to breakpoint `MAIN__'."
+    continue
+}
+
+gdb_breakpoint [gdb_get_line_number "stop-here"]
+gdb_continue_to_breakpoint "stop-here" ".*stop-here.*"
+
+gdb_print_expr_at_depths "var" {"\\(\\.\\.\\.\\)" \
+				    "\\( d = \\(\\.\\.\\.\\) \\)" \
+				    "\\( d = \\( c = \\(\\.\\.\\.\\) \\) \\)" \
+				    "\\( d = \\( c = \\( b = \\(\\.\\.\\.\\), array = \\(\\.\\.\\.\\) \\) \\) \\)" \
+				    "\\( d = \\( c = \\( b = \\( a = 1 \\), array = \\(0, 0, 0, 0, 0\\) \\) \\) \\)" }
diff --git a/gdb/testsuite/gdb.fortran/max-depth.f90 b/gdb/testsuite/gdb.fortran/max-depth.f90
new file mode 100644
index 00000000000..6af105d37e1
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/max-depth.f90
@@ -0,0 +1,40 @@
+! 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 2 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/>.
+
+program max_depth_test
+  type :: s1
+     integer :: a
+  end type s1
+
+  type :: s2
+     type (s1) :: b
+     integer :: array (5)
+  end type s2
+
+  type :: s3
+     type (s2) :: c
+  end type s3
+
+  type :: s4
+     type (s3) :: d
+  end type s4
+
+  logical :: l
+  type (s4) :: var
+
+  var%d%c%b%a = 1
+  var%d%c%array = 0
+  l = .FALSE.					! stop-here
+end program max_depth_test
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.c b/gdb/testsuite/gdb.python/py-nested-maps.c
new file mode 100644
index 00000000000..29503908eae
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.c
@@ -0,0 +1,122 @@
+/* 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/>.  */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define FIXED_MAP_SIZE 10
+
+struct key_t
+{
+  int a;
+  int b;
+};
+
+struct value_t
+{
+  int x;
+  int y;
+  int z;
+};
+
+struct map_t
+{
+  const char *name;
+  int length;
+  struct key_t *keys;
+  struct value_t *values;
+};
+
+struct map_map_t
+{
+  int length;
+  struct map_t **values;
+};
+
+struct map_t *
+create_map (const char *name)
+{
+  struct map_t *m = malloc (sizeof (struct map_t));
+  m->name = strdup (name);
+  m->length = 0;
+  m->keys = NULL;
+  m->values = NULL;
+}
+
+void
+add_map_element (struct map_t *m, struct key_t k, struct value_t v)
+{
+  if (m->length == 0)
+    {
+      m->keys = malloc (sizeof (struct key_t) * FIXED_MAP_SIZE);
+      m->values = malloc (sizeof (struct value_t) * FIXED_MAP_SIZE);
+    }
+
+  m->keys[m->length] = k;
+  m->values[m->length] = v;
+  m->length++;
+}
+
+struct map_map_t *
+create_map_map ()
+{
+  struct map_map_t *mm = malloc (sizeof (struct map_map_t));
+  mm->length = 0;
+  mm->values = NULL;
+}
+
+void
+add_map_map_element (struct map_map_t *mm, struct map_t *map)
+{
+  if (mm->length == 0)
+    mm->values = malloc (sizeof (struct map_t *) * FIXED_MAP_SIZE);
+
+  mm->values[mm->length] = map;
+  mm->length++;
+}
+
+int
+main ()
+{
+  struct map_t *m1 = create_map ("m1");
+  struct key_t k1 = {3, 4};
+  struct key_t k2 = {4, 5};
+  struct key_t k3 = {5, 6};
+  struct key_t k4 = {6, 7};
+  struct key_t k5 = {7, 8};
+  struct key_t k6 = {8, 9};
+  struct value_t v1 = {0, 1, 2};
+  struct value_t v2 = {3, 4, 5};
+  struct value_t v3 = {6, 7, 8};
+  struct value_t v4 = {9, 0, 1};
+  struct value_t v5 = {2, 3, 4};
+  struct value_t v6 = {5, 6, 7};
+  add_map_element (m1, k1, v1);
+  add_map_element (m1, k2, v2);
+  add_map_element (m1, k3, v3);
+
+  struct map_t *m2 = create_map ("m2");
+  add_map_element (m2, k4, v4);
+  add_map_element (m2, k5, v5);
+  add_map_element (m2, k6, v6);
+
+  struct map_map_t *mm = create_map_map ();
+  add_map_map_element (mm, m1);
+  add_map_map_element (mm, m2);
+
+  return 0; /* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.exp b/gdb/testsuite/gdb.python/py-nested-maps.exp
new file mode 100644
index 00000000000..146b9cab075
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.exp
@@ -0,0 +1,211 @@
+# Copyright (C) 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 tests GDB's python pretty printing of nested map like
+# structures using structures as keys and values.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug}] } {
+    return -1
+}
+
+if ![runto_main ] then {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "run to testing point" ".*Break here.*"
+
+set remote_python_file [gdb_remote_download host \
+			    ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+# Test printing with 'set print pretty off'
+gdb_test_no_output "set print pretty off"
+with_test_prefix "pretty=off" {
+    gdb_print_expr_at_depths "*m1" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     "\{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
+	     "\{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
+	    ]
+
+    gdb_print_expr_at_depths "*mm" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
+	    ]
+}
+
+# Now again, but with 'set print pretty on'
+gdb_test_no_output "set print pretty on"
+with_test_prefix "pretty=on" {
+    gdb_print_expr_at_depths "*m1" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[\{" \
+		  "    a = 3," \
+		  "    b = 4" \
+		  "  \}\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[\{" \
+		  "    a = 4," \
+		  "    b = 5" \
+		  "  \}\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[\{" \
+		  "    a = 5," \
+		  "    b = 6" \
+		  "  \}\\\] = \{\\.\\.\\.\}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[\{" \
+		  "    a = 3," \
+		  "    b = 4" \
+		  "  \}\\\] = \{" \
+		  "    x = 0," \
+		  "    y = 1," \
+		  "    z = 2" \
+		  "  \}," \
+		  "  \\\[\{" \
+		  "    a = 4," \
+		  "    b = 5" \
+		  "  \}\\\] = \{" \
+		  "    x = 3," \
+		  "    y = 4," \
+		  "    z = 5" \
+		  "  \}," \
+		  "  \\\[\{" \
+		  "    a = 5," \
+		  "    b = 6" \
+		  "  \}\\\] = \{" \
+		  "    x = 6," \
+		  "    y = 7," \
+		  "    z = 8" \
+		  "  \}" \
+		  "\}" ] \
+	    ]
+
+     gdb_print_expr_at_depths "*mm" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 3," \
+		  "      b = 4" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 4," \
+		  "      b = 5" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 5," \
+		  "      b = 6" \
+		  "    \}\\\] = \{\\.\\.\\.\}" \
+		  "  \}," \
+		  "  \\\[$hex \"m2\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 6," \
+		  "      b = 7" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 7," \
+		  "      b = 8" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 8," \
+		  "      b = 9" \
+		  "    \}\\\] = \{\\.\\.\\.\}" \
+		  "  \}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 3," \
+		  "      b = 4" \
+		  "    \}\\\] = \{" \
+		  "      x = 0," \
+		  "      y = 1," \
+		  "      z = 2" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 4," \
+		  "      b = 5" \
+		  "    \}\\\] = \{" \
+		  "      x = 3," \
+		  "      y = 4," \
+		  "      z = 5" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 5," \
+		  "      b = 6" \
+		  "    \}\\\] = \{" \
+		  "      x = 6," \
+		  "      y = 7," \
+		  "      z = 8" \
+		  "    \}" \
+		  "  \}," \
+		  "  \\\[$hex \"m2\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 6," \
+		  "      b = 7" \
+		  "    \}\\\] = \{" \
+		  "      x = 9," \
+		  "      y = 0," \
+		  "      z = 1" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 7," \
+		  "      b = 8" \
+		  "    \}\\\] = \{" \
+		  "      x = 2," \
+		  "      y = 3," \
+		  "      z = 4" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 8," \
+		  "      b = 9" \
+		  "    \}\\\] = \{" \
+		  "      x = 5," \
+		  "      y = 6," \
+		  "      z = 7" \
+		  "    \}" \
+		  "  \}" \
+		  "\}" ] \
+	    ]
+}
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.py b/gdb/testsuite/gdb.python/py-nested-maps.py
new file mode 100644
index 00000000000..e6489d5636c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.py
@@ -0,0 +1,129 @@
+# Copyright (C) 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 GDB's printing of
+# nested map like structures.
+
+import re
+import gdb
+
+def _iterator1 (pointer, len):
+    while len > 0:
+        map = pointer.dereference()
+        yield ('', map['name'])
+        yield ('', map.dereference())
+        pointer += 1
+        len -= 1
+
+def _iterator2 (pointer1, pointer2, len):
+    while len > 0:
+        yield ("", pointer1.dereference())
+        yield ("", pointer2.dereference())
+        pointer1 += 1
+        pointer2 += 1
+        len -= 1
+
+class pp_map (object):
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return None
+
+    def children(self):
+        return _iterator2(self.val['keys'],
+                          self.val['values'],
+                          self.val['length'])
+
+    def display_hint (self):
+        return 'map'
+
+class pp_map_map (object):
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        return None
+
+    def children(self):
+        return _iterator1(self.val['values'],
+                          self.val['length'])
+
+    def display_hint (self):
+        return 'map'
+
+def 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 == None:
+        return None
+
+    # Iterate over local dictionary of types to determine
+    # if a printer is registered for that type.  Return an
+    # instantiation of the printer if found.
+    for function in pretty_printers_dict:
+        if function.match (typename):
+            return pretty_printers_dict[function] (val)
+
+    # Cannot find a pretty printer.  Return None.
+    return None
+
+# Lookup a printer for VAL in the typedefs dict.
+def lookup_typedefs_function (val):
+    "Look-up and return a pretty-printer that can print val (typedefs)."
+
+    # Get the type.
+    type = val.type
+
+    if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF:
+        return None
+
+    # Iterate over local dictionary of typedef types to determine if a
+    # printer is registered for that type.  Return an instantiation of
+    # the printer if found.
+    for function in typedefs_pretty_printers_dict:
+        if function.match (type.name):
+            return typedefs_pretty_printers_dict[function] (val)
+
+    # Cannot find a pretty printer.
+    return None
+
+def register_pretty_printers ():
+    pretty_printers_dict[re.compile ('^struct map_t$')] = pp_map
+    pretty_printers_dict[re.compile ('^map_t$')] = pp_map
+    pretty_printers_dict[re.compile ('^struct map_map_t$')] = pp_map_map
+    pretty_printers_dict[re.compile ('^map_map_t$')] = pp_map_map
+
+# Dict for struct types with typedefs fully stripped.
+pretty_printers_dict = {}
+# Dict for typedef types.
+typedefs_pretty_printers_dict = {}
+
+register_pretty_printers ()
+gdb.pretty_printers.append (lookup_function)
+gdb.pretty_printers.append (lookup_typedefs_function)
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index e3c6b2d6f5e..629107251b7 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -1390,6 +1390,36 @@ proc gdb_test_stdio {command inferior_pattern {gdb_pattern ""} {message ""}} {
     return $res
 }
 
+# get_print_expr_at_depths EXP OUTPUTS
+#
+# Used for testing 'set print max-depth'.  Prints the expression EXP
+# with 'set print max-depth' set to various depths.  OUTPUTS is a list
+# of `n` different patterns to match at each of the depths from 0 to
+# (`n` - 1).
+#
+# This proc does one final check with the max-depth set to 'unlimited'
+# which is tested against the last pattern in the OUTPUTS list.  The
+# OUTPUTS list is therefore required to match every depth from 0 to a
+# depth where the whole of EXP is printed with no ellipsis.
+#
+# This proc leaves the 'set print max-depth' set to 'unlimited'.
+proc gdb_print_expr_at_depths {exp outputs} {
+    for { set depth 0 } { $depth <= [llength $outputs] } { incr depth } {
+	if { $depth == [llength $outputs] } {
+	    set expected_result [lindex $outputs [expr [llength $outputs] - 1]]
+	    set depth_string "unlimited"
+	} else {
+	    set expected_result [lindex $outputs $depth]
+	    set depth_string $depth
+	}
+
+	with_test_prefix "exp='$exp': depth=${depth_string}" {
+	    gdb_test_no_output "set print max-depth ${depth_string}"
+	    gdb_test "p $exp" "$expected_result"
+	}
+    }
+}
+
 \f
 
 # Issue a PASS and return true if evaluating CONDITION in the caller's
diff --git a/gdb/valprint.c b/gdb/valprint.c
index 10020901c2d..e47ac981ce8 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -28,6 +28,7 @@
 #include "annotate.h"
 #include "valprint.h"
 #include "target-float.h"
+#include "c-lang.h" /* For c_textual_element_type.  */
 #include "extension.h"
 #include "ada-lang.h"
 #include "gdb_obstack.h"
@@ -88,6 +89,7 @@ static void val_print_type_code_flags (struct type *type,
 				       struct ui_file *stream);
 
 #define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
+#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
 
 struct value_print_options user_print_options =
 {
@@ -109,7 +111,8 @@ struct value_print_options user_print_options =
   1,				/* pascal_static_field_print */
   0,				/* raw */
   0,				/* summary */
-  1				/* symbol_print */
+  1,				/* symbol_print */
+  PRINT_MAX_DEPTH_DEFAULT	/* max_depth */
 };
 
 /* Initialize *OPTS to be a copy of the user print options.  */
@@ -281,6 +284,39 @@ val_print_scalar_type_p (struct type *type)
     }
 }
 
+/* A helper function for val_print.  When printing with limited depth we
+   want to print string and scalar arguments, but not aggregate arguments.
+   This function distinguishes between the two.  */
+
+static bool
+val_print_scalar_or_string_type_p (struct type *type)
+{
+  /* Resolve the real type.  */
+  type = check_typedef (type);
+  while (TYPE_CODE (type) == TYPE_CODE_REF)
+    {
+      type = TYPE_TARGET_TYPE (type);
+      type = check_typedef (type);
+    }
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+      {
+	/* See if target type looks like a string.  */
+	struct type * array_target_type = TYPE_TARGET_TYPE (type);
+	return (TYPE_LENGTH (type) > 0
+		&& TYPE_LENGTH (array_target_type) > 0
+		&& c_textual_element_type (array_target_type, 0));
+      }
+    case TYPE_CODE_STRING:
+      return true;
+    default:
+      /* Check for scalar.  */
+      return val_print_scalar_type_p(type);
+    }
+}
+
 /* See its definition in value.h.  */
 
 int
@@ -1054,6 +1090,15 @@ val_print (struct type *type, LONGEST embedded_offset,
       return;
     }
 
+  if (!val_print_scalar_or_string_type_p (type)
+      && options->max_depth > -1
+      && recurse >= options->max_depth)
+    {
+      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);
+      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
+      return;
+    }
+
   TRY
     {
       language->la_val_print (type, embedded_offset, address,
@@ -2860,6 +2905,15 @@ val_print_string (struct type *elttype, const char *encoding,
 
   return (bytes_read / width);
 }
+
+/* Handle 'show print max-depth'.  */
+
+static void
+show_print_max_depth (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Maximum print depth is %s.\n"), value);
+}
 \f
 
 /* The 'set input-radix' command writes to this auxiliary variable.
@@ -3153,4 +3207,14 @@ Use 'show input-radix' or 'show output-radix' to independently show each."),
 Set printing of array indexes."), _("\
 Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
                            &setprintlist, &showprintlist);
+
+  add_setshow_zuinteger_unlimited_cmd ("max-depth", class_support,
+                            &user_print_options.max_depth, _("\
+Set maximum print depth for nested structures, unions and arrays."), _("\
+Show maximum print depth for nested structures, unions, and arrays."), _("\
+When structures, unions, or arrays are nested beyond this depth then they\n\
+will be replaced with either '{...}' or '(...)' depending on the language.\n\
+Use 'set print max-depth unlimited' to print the complete structure."),
+				       NULL, show_print_max_depth,
+				       &setprintlist, &showprintlist);
 }
diff --git a/gdb/valprint.h b/gdb/valprint.h
index db99b52b2ad..a0395f67a07 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -92,6 +92,9 @@ struct value_print_options
   /* If nonzero, when printing a pointer, print the symbol to which it
      points, if any.  */
   int symbol_print;
+
+  /* Maximum print depth when printing nested aggregates.  */
+  int max_depth;
 };
 
 /* The global print options set by the user.  In general this should
-- 
2.14.5

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

* Re: [PATCH 2/2] gdb: Introduce 'print max-depth' feature
  2019-03-27 21:53   ` [PATCH 2/2] gdb: Introduce 'print max-depth' feature Andrew Burgess
@ 2019-03-28 12:42     ` Pedro Alves
  2019-03-28 16:08     ` Eli Zaretskii
  1 sibling, 0 replies; 33+ messages in thread
From: Pedro Alves @ 2019-03-28 12:42 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: richard.bunt

Noice!

Overall looks good to me.

I think you're missing tests for C++ inheritance (all of regular, multiple and
virtual).

E.g,. with:

 struct A { int a; int x; };
 struct B : A { int b; int x; };
 struct C : B { int c; int x; };

 C c;

Today we print:

 (gdb) p c
 $1 = {<B> = {<A> = {a = 0, x = 0}, b = 0, x = 0}, c = 0, x = 0}

I suppose max-depth affects this as well.  But given no tests,
can't be sure.  :-)

Some nits pointed out below.

On 03/27/2019 09:53 PM, Andrew Burgess wrote:
> Introduce a new print setting max-depth which can be set with 'set
> print max-depth DEPTH'.  The default value of DEPTH is 20, but this
> can also be set to unlimited.
> 
> When GDB is printing a value containing nested structures GDB will
> stop descending at depth DEPTH.  Here is a small example:
> 
>     typedef struct s1 { int a; } s1;
>     typedef struct s2 { s1 b; } s2;
>     typedef struct s3 { s2 c; } s3;
>     typedef struct s4 { s3 d; } s4;
> 
>     s4 var = { { { { 3 } } } };
> 
> The following table shows how various depth settings effect printing
> of 'var':

s/effect/affect/


> 
>     | Depth Setting | Result of 'p var'              |
>     |---------------+--------------------------------|
>     |     Unlimited | $1 = {d = {c = {b = {a = 3}}}} |
>     |             4 | $1 = {d = {c = {b = {a = 3}}}} |
>     |             3 | $1 = {d = {c = {b = {...}}}}   |
>     |             2 | $1 = {d = {c = {...}}}         |
>     |             1 | $1 = {d = {...}}               |
>     |             0 | $1 = {...}                     |
> 
> Only structures, unions, and arrays are replaced in this way, scalars
> and strings are not replaced.
> 
> The replacement is counted from the level at which you print, not from
> the top level of the structure.  So, consider the above example and
> this GDB session:
> 
>     (gdb) set print max-depth 2
>     (gdb) p var
>     $1 = {d = {c = {...}}}
>     (gdb) p var.d
>     $2 = {c = {b = {...}}}
>     (gdb) p var.d.c
>     $3 = {b = {a = 3}}
> 
> Setting the max-depth to 2 doesn't prevent the user from exploring
> deeper into 'var' by asking for specific sub-fields to be printed.
> 
> The motivation behind this feature is to try and give the user more
> control over how much is printed when examining large, complex data
> structures.
> 
> The default max-depth of 20 means that there is a change in GDB's
> default behaviour.  Someone printing a data structure with 20 levels
> of nesting will now see '{...}' instead of their data, they would need
> to adjust the max depth, or call print again naming a specific field
> in order to dig deeper into their data structure.  If this is
> considered a problem then we could increase the default, or even make
> the default unlimited.
> 
> This commit relies on the previous commit, which added a new field to
> the language structure, this new field was a string that contained the
> pattern that should be used when a structure/union/array is replaced
> in the output, this allows languages to use a syntax that is more
> appropriate, mostly this will be selecting the correct types of
> bracket '(...)' or '{...}', both of which are currently in use.


Could you say something about if/how this affects Python, Guile, and MI ?


>  * GDB and GDBserver now support access to additional registers on
> diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
> index 443c3b06dac..71da2b19755 100644
> --- a/gdb/cp-valprint.c
> +++ b/gdb/cp-valprint.c
> @@ -272,10 +272,23 @@ cp_print_value_fields (struct type *type, struct type *real_type,
>  				   current_language->la_language,
>  				   DMGL_PARAMS | DMGL_ANSI);
>  	  annotate_field_name_end ();
> +
> +	  /* We tweak various options in a few cases below.  */
> +	  struct value_print_options options_copy = *options;
> +	  struct value_print_options *opts = &options_copy;

You could drop the redundant "struct".

> +
>  	  /* Do not print leading '=' in case of anonymous
>  	     unions.  */
>  	  if (strcmp (TYPE_FIELD_NAME (type, i), ""))
>  	    fputs_filtered (" = ", stream);
> +	  else
> +	    {
> +	      /* If this is an anonymous field then we want to consider it
> +		 as though it is at its parents depth when it comes to the

"parent's"

> +		 max print depth.  */
> +	      if (opts->max_depth != -1 && opts->max_depth < INT_MAX)
> +		++opts->max_depth;
> +	    }


>  @defun pretty_printer.display_hint (self)
> diff --git a/gdb/guile/scm-pretty-print.c b/gdb/guile/scm-pretty-print.c
> index e5096944b68..219e0ef63ad 100644
> --- a/gdb/guile/scm-pretty-print.c
> +++ b/gdb/guile/scm-pretty-print.c
> @@ -744,6 +744,16 @@ ppscm_print_children (SCM printer, enum display_hint hint,
>        return;
>      }
>  
> +  if (options->max_depth > -1
> +      && recurse >= options->max_depth)
> +    {
> +      if (language->la_language == language_fortran)
> +	fputs_filtered ("(...)", stream);
> +      else
> +	fputs_filtered ("{...}", stream);

Shouldn't this be using la_struct_too_deep_ellipsis here too?

> +      return;
> +    }

> diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
> index e64d1f88af8..504644143f3 100644
> --- a/gdb/python/py-prettyprint.c
> +++ b/gdb/python/py-prettyprint.c
> @@ -361,6 +361,16 @@ print_children (PyObject *printer, const char *hint,
>    if (! PyObject_HasAttr (printer, gdbpy_children_cst))
>      return;
>  
> +  if (options->max_depth > -1
> +      && recurse >= options->max_depth)
> +    {
> +      if (language->la_language == language_fortran)
> +	fputs_filtered ("(...)", stream);
> +      else
> +	fputs_filtered ("{...}", stream);

Ditto.

> +      return;
> +    }
> +


> diff --git a/gdb/testsuite/gdb.base/max-depth.exp b/gdb/testsuite/gdb.base/max-depth.exp
> new file mode 100644
> index 00000000000..777f66fab4e
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/max-depth.exp

> +proc compile_and_run_tests { lang } {
> +    global testfile
> +    global srcfile
> +    global binfile
> +    global subdir
> +    global srcdir
> +
> +    standard_testfile .c
> +    set dir "$lang"
> +
> +    # Create the additional flags

Add period.

> +    set flags "debug"
> +    lappend flags $lang
> +
> +    set binfile [standard_output_file ${dir}/${testfile}]
> +    if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable "${flags}"] != "" } {

Spurious double space after "if".

Wonder if this could use build_executable or prepare_for_testing (eliminating
the clean_restart below).

> +	unresolved "failed to compile"
> +	return 0
> +    }
> +
> +    # Start with a fresh gdb.
> +    clean_restart ${binfile}
> +
> +    # Advance to main

Add period.

> +    if { ![runto_main] } then {
> +	fail "can't run to main"
> +	return 0
> +    }


> +    # Check handling of typedefs.
> +    gdb_print_expr_at_depths "s6" {"{...}" \
> +				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
> +				       "{{raw = \\\{0, 0, 0\\\}, {x = 0, y = 0, z = 0}}}"}
> +
> +    # Multiple anonymous structures in parallel.
> +    gdb_print_expr_at_depths "s7" {"{...}" \
> +				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}" \
> +				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}"}
> +
> +    # Flip flop between named and anonymous. Expected to unfurl to the

Double space before "Expected".

> +++ b/gdb/testsuite/gdb.python/py-nested-maps.c
> @@ -0,0 +1,122 @@

> +
> +struct map_map_t *
> +create_map_map ()

create_map_map (void)

> +{

> diff --git a/gdb/testsuite/gdb.python/py-nested-maps.exp b/gdb/testsuite/gdb.python/py-nested-maps.exp
> new file mode 100644
> index 00000000000..146b9cab075
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-nested-maps.exp
> @@ -0,0 +1,211 @@
> +# Copyright (C) 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 tests GDB's python pretty printing of nested map like
> +# structures using structures as keys and values.
> +

Should this say something about depth?


> +gdb_breakpoint [gdb_get_line_number "Break here"]
> +gdb_continue_to_breakpoint "run to testing point" ".*Break here.*"
> +
> +set remote_python_file [gdb_remote_download host \
> +			    ${srcdir}/${subdir}/${testfile}.py]
> +gdb_test_no_output "source ${remote_python_file}" "load python file"
> +
> +# Test printing with 'set print pretty off'

Missing period.

> +gdb_test_no_output "set print pretty off"
> +with_test_prefix "pretty=off" {
> +    gdb_print_expr_at_depths "*m1" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     "\{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
> +	     "\{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
> +	    ]
> +
> +    gdb_print_expr_at_depths "*mm" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
> +	    ]
> +}
> +
> +# Now again, but with 'set print pretty on'

Missing period.

>  
>  # Issue a PASS and return true if evaluating CONDITION in the caller's
> diff --git a/gdb/valprint.c b/gdb/valprint.c
> index 10020901c2d..e47ac981ce8 100644
> --- a/gdb/valprint.c
> +++ b/gdb/valprint.c
> @@ -28,6 +28,7 @@
>  #include "annotate.h"
>  #include "valprint.h"
>  #include "target-float.h"
> +#include "c-lang.h" /* For c_textual_element_type.  */
>  #include "extension.h"
>  #include "ada-lang.h"
>  #include "gdb_obstack.h"
> @@ -88,6 +89,7 @@ static void val_print_type_code_flags (struct type *type,
>  				       struct ui_file *stream);
>  
>  #define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
> +#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
>  
>  struct value_print_options user_print_options =
>  {
> @@ -109,7 +111,8 @@ struct value_print_options user_print_options =
>    1,				/* pascal_static_field_print */
>    0,				/* raw */
>    0,				/* summary */
> -  1				/* symbol_print */
> +  1,				/* symbol_print */
> +  PRINT_MAX_DEPTH_DEFAULT	/* max_depth */
>  };
>  
>  /* Initialize *OPTS to be a copy of the user print options.  */
> @@ -281,6 +284,39 @@ val_print_scalar_type_p (struct type *type)
>      }
>  }
>  
> +/* A helper function for val_print.  When printing with limited depth we
> +   want to print string and scalar arguments, but not aggregate arguments.
> +   This function distinguishes between the two.  */
> +
> +static bool
> +val_print_scalar_or_string_type_p (struct type *type)
> +{
> +  /* Resolve the real type.  */
> +  type = check_typedef (type);
> +  while (TYPE_CODE (type) == TYPE_CODE_REF)
> +    {
> +      type = TYPE_TARGET_TYPE (type);
> +      type = check_typedef (type);
> +    }
> +
> +  switch (TYPE_CODE (type))
> +    {
> +    case TYPE_CODE_ARRAY:
> +      {
> +	/* See if target type looks like a string.  */
> +	struct type * array_target_type = TYPE_TARGET_TYPE (type);

Spurious space after "*".

> +	return (TYPE_LENGTH (type) > 0
> +		&& TYPE_LENGTH (array_target_type) > 0
> +		&& c_textual_element_type (array_target_type, 0));

Is using c_textual_element_type safe / the right thing to do
for all languages?


> +      }
> +    case TYPE_CODE_STRING:
> +      return true;
> +    default:
> +      /* Check for scalar.  */
> +      return val_print_scalar_type_p(type);
> +    }
> +}
> +
>  /* See its definition in value.h.  */
>  
>  int
> @@ -1054,6 +1090,15 @@ val_print (struct type *type, LONGEST embedded_offset,
>        return;
>      }
>  
> +  if (!val_print_scalar_or_string_type_p (type)
> +      && options->max_depth > -1
> +      && recurse >= options->max_depth)
> +    {
> +      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);

Maybe put this assertion / validation where we register languages
instead, so you'd do this only once and in one place, instead of
potentially at all usage spots?

> +      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
> +      return;
> +    }
> +
Thanks,
Pedro Alves

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

* Re: [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis
  2019-03-27 21:53   ` [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
@ 2019-03-28 12:43     ` Pedro Alves
  0 siblings, 0 replies; 33+ messages in thread
From: Pedro Alves @ 2019-03-28 12:43 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: richard.bunt

Seems fine.

Thanks,
Pedro Alves

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

* Re: [PATCH 0/2] Add new 'print max-depth' feature
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
  2019-03-27 21:53   ` [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
  2019-03-27 21:53   ` [PATCH 2/2] gdb: Introduce 'print max-depth' feature Andrew Burgess
@ 2019-03-28 12:47   ` Pedro Alves
  2019-03-28 22:48     ` Andrew Burgess
  2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
                     ` (4 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Pedro Alves @ 2019-03-28 12:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: richard.bunt

BTW, wasn't there some patch being reviewed that added explicit
control of the "set print" options to the Python API?  I can't seem to
find it right now, but I think you'll want to coordinate, to make
sure that "depth" is also added to the API.

Thanks,
Pedro Alves

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

* Re: [PATCH 2/2] gdb: Introduce 'print max-depth' feature
  2019-03-27 21:53   ` [PATCH 2/2] gdb: Introduce 'print max-depth' feature Andrew Burgess
  2019-03-28 12:42     ` Pedro Alves
@ 2019-03-28 16:08     ` Eli Zaretskii
  1 sibling, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2019-03-28 16:08 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, richard.bunt

> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: richard.bunt@arm.com,	Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Wed, 27 Mar 2019 21:53:24 +0000
> 
> +* New commands
> +
> +set print max-depth
> +show print max-depth
> +  Allows deeply nested structures to be simplified when printing by
> +  replacing deeply nested parts (beyond the max-depth) with ellipses.
> +  The default max-depth is 20, but this can be set to unlimited to get
> +  the old behaviour back.

This part is OK, but please use "behavior", as our convention is to
use US English spelling.

> +@noindent
> +and with @code{set print max-depth unlimited} in effect @samp{p var}
> +would print

I think this and other similar sentences should begin with a capital
letter.

The documentation parts are okay with these nits fixed.

Thanks.

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

* Re: [PATCH 0/2] Add new 'print max-depth' feature
  2019-03-28 12:47   ` [PATCH 0/2] Add new " Pedro Alves
@ 2019-03-28 22:48     ` Andrew Burgess
  2019-03-28 23:47       ` Marco Barisione
  0 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-03-28 22:48 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, richard.bunt

* Pedro Alves <palves@redhat.com> [2019-03-28 12:47:11 +0000]:

> BTW, wasn't there some patch being reviewed that added explicit
> control of the "set print" options to the Python API?  I can't seem to
> find it right now, but I think you'll want to coordinate, to make
> sure that "depth" is also added to the API.

I've taken a look and I can't find anything.  I'll look again
tomorrow, but any pointers in the right direction would be great.

Thanks

Andrew

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

* Re: [PATCH 0/2] Add new 'print max-depth' feature
  2019-03-28 22:48     ` Andrew Burgess
@ 2019-03-28 23:47       ` Marco Barisione
  0 siblings, 0 replies; 33+ messages in thread
From: Marco Barisione @ 2019-03-28 23:47 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Pedro Alves, gdb-patches, richard.bunt

On 28 Mar 2019, at 23:48, Andrew Burgess <andrew.burgess@embecosm.com> wrote:
> I've taken a look and I can't find anything.  I'll look again
> tomorrow, but any pointers in the right direction would be great.

Hi Andrew,

It’s my patch, which I hope will be merged soon, look for the thread title “Add gdb.Value.format_string ()”.
I did notice you patches today and I wanted to propose some coordination as well.

My patch adds a gdb.Value.format_string method which accepts a series of keyword-only arguments to set printing options. Basically it exposes stuff from the value_print_options structure to Python.

I guess that a new max_depth argument would fit in well and easily.

-- 
Marco Barisione

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

* [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
                     ` (5 preceding siblings ...)
  2019-04-16 23:06   ` [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool Andrew Burgess
@ 2019-04-16 23:06   ` Andrew Burgess
  2019-04-19 14:37     ` Tom Tromey
  2019-04-16 23:06   ` [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p Andrew Burgess
  7 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is preparation work for a later commit, and by itself
makes no user visible change to GDB.  I've split this work into a
separate commit in order to make code review easier.

This commit adds a new field 'la_struct_too_deep_ellipsis' to the
language struct, this string will be used in the next commit to print
a language specific string from within the generic value printing
code.

In this commit I add the new field and initialise it for each
language, however at this stage the new field is never used.

gdb/ChangeLog:

	* language.h (struct language_defn) <la_struct_too_deep_ellipsis>:
	New field.
	* ada-lang.c (ada_language_defn): Initialise new field.
	* c-lang.c (c_language_defn): Likewise.
	(cplus_language_defn): Likewise.
	(asm_language_defn): Likewise.
	(minimal_language_defn): Likewise.
	* d-lang.c (d_language_defn): Likewise.
	* f-lang.c (f_language_defn): Likewise.
	* go-lang.c (go_language_defn): Likewise.
	* language.c (unknown_language_defn): Likewise.
	(auto_language_defn): Likewise.
	* m2-lang.c (m2_language_defn): Likewise.
	* objc-lang.c (objc_language_defn): Likewise.
	* opencl-lang.c (opencl_language_defn): Likewise.
	* p-lang.c (pascal_language_defn): Likewise.
	* rust-lang.c (rust_language_defn): Likewise.
---
 gdb/ChangeLog     | 20 ++++++++++++++++++++
 gdb/ada-lang.c    |  3 ++-
 gdb/c-lang.c      | 12 ++++++++----
 gdb/d-lang.c      |  3 ++-
 gdb/f-lang.c      |  3 ++-
 gdb/go-lang.c     |  3 ++-
 gdb/language.c    |  6 ++++--
 gdb/language.h    |  6 ++++++
 gdb/m2-lang.c     |  3 ++-
 gdb/objc-lang.c   |  3 ++-
 gdb/opencl-lang.c |  3 ++-
 gdb/p-lang.c      |  3 ++-
 gdb/rust-lang.c   |  3 ++-
 13 files changed, 56 insertions(+), 15 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 622cf59de68..fddb0d7278c 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14380,7 +14380,8 @@ extern const struct language_defn ada_language_defn = {
   default_search_name_hash,
   &ada_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "(...)"			/* la_struct_too_deep_ellipsis */
 };
 
 /* Command-list for the "set/show ada" prefix command.  */
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 33506f1d1ed..3be5ef57252 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -873,7 +873,8 @@ extern const struct language_defn c_language_defn =
   default_search_name_hash,
   &c_varobj_ops,
   c_get_compile_context,
-  c_compute_program
+  c_compute_program,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 enum cplus_primitive_types {
@@ -1017,7 +1018,8 @@ extern const struct language_defn cplus_language_defn =
   cp_search_name_hash,
   &cplus_varobj_ops,
   cplus_get_compile_context,
-  cplus_compute_program
+  cplus_compute_program,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 static const char *asm_extensions[] =
@@ -1070,7 +1072,8 @@ extern const struct language_defn asm_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 /* The following language_defn does not represent a real language.
@@ -1123,5 +1126,6 @@ extern const struct language_defn minimal_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 08b638009ff..751c521a75d 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -250,7 +250,8 @@ extern const struct language_defn d_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 /* Build all D language types for the specified architecture.  */
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index f3a806e7a6f..77eb50a0761 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -377,7 +377,8 @@ extern const struct language_defn f_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "(...)"			/* la_struct_too_deep_ellipsis */
 };
 
 static void *
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index f74cee4ccd8..b8617cb162d 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -611,7 +611,8 @@ extern const struct language_defn go_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 static void *
diff --git a/gdb/language.c b/gdb/language.c
index 954e4c200f0..da8dd1bf7ae 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -876,7 +876,8 @@ const struct language_defn unknown_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 /* These two structs define fake entries for the "local" and "auto"
@@ -926,7 +927,8 @@ const struct language_defn auto_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 \f
diff --git a/gdb/language.h b/gdb/language.h
index 3e0bc9d0d46..261f5a33cc7 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -449,6 +449,12 @@ struct language_defn
 				       struct gdbarch *gdbarch,
 				       const struct block *expr_block,
 				       CORE_ADDR expr_pc);
+
+    /* This string is used by the 'set print max-depth' setting.  When GDB
+       replaces a struct or union (during value printing) that is "too
+       deep" this string is displayed instead.  */
+    const char *la_struct_too_deep_ellipsis;
+
   };
 
 /* Pointer to the language_defn for our current language.  This pointer
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index b4eb2628571..9f2a97d54fd 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -398,7 +398,8 @@ extern const struct language_defn m2_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 static void *
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index 1853a69e754..af92e55a437 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -408,7 +408,8 @@ extern const struct language_defn objc_language_defn = {
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 /*
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index c1ea66eea39..c95bf05a98b 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1086,7 +1086,8 @@ extern const struct language_defn opencl_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
 static void *
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 9340861761b..0b85f70dfd2 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -459,5 +459,6 @@ extern const struct language_defn pascal_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 9a123c338ef..8faafd49cdd 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -2141,5 +2141,6 @@ extern const struct language_defn rust_language_defn =
   default_search_name_hash,
   &default_varobj_ops,
   NULL,
-  NULL
+  NULL,
+  "{...}"			/* la_struct_too_deep_ellipsis */
 };
-- 
2.14.5

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

* [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
                     ` (6 preceding siblings ...)
  2019-04-16 23:06   ` [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
@ 2019-04-16 23:06   ` Andrew Burgess
  2019-04-18 17:08     ` Pedro Alves
  2019-04-19 14:45     ` Tom Tromey
  7 siblings, 2 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit is preparation work for the next commit, and by itself
makes no user visible change to GDB.  I've split this work into a
separate commit in order to make code review easier.

This commit adds a new field 'la_is_string_type_p' to the language
struct, this predicate will return true if a type is a string type for
the given language.

Some languages already have a "is this a string" predicate that I was
able to reuse, while for other languages I've had to add a new
predicate.  In this case I took inspiration from the value printing
code for that language - what different conditions would result in
printing something as a string.

A default "is this a string" method has also been added that looks for
TYPE_CODE_STRING, this is the fallback I've used for a couple of
languages.

In this commit I add the new field and initialise it for each
language, however at this stage the new field is never used.

gdb/ChangeLog:

	* ada-lang.c (ada_language_defn): Initialise new field.
	* c-lang.c (c_is_string_type_p): New function.
	(c_language_defn): Initialise new field.
	(cplus_language_defn): Initialise new field.
	(asm_language_defn): Initialise new field.
	(minimal_language_defn): Initialise new field.
	* c-lang.h (c_is_string_type_p): Declare new function.
	* d-lang.c (d_language_defn): Initialise new field.
	* f-lang.c (f_is_string_type_p): New function.
	(f_language_defn): Initialise new field.
	* go-lang.c (go_is_string_type_p): New function.
	(go_language_defn): Initialise new field.
	* language.c (default_is_string_type_p): New function.
	(unknown_language_defn): Initialise new field.
	(auto_language_defn): Initialise new field.
	* language.h (struct language_defn) <la_is_string_type_p>: New
	member variable.
	(default_is_string_type_p): Declare new function.
	* m2-lang.c (m2_language_defn): Initialise new field.
	* objc-lang.c (objc_language_defn): Initialise new field.
	* opencl-lang.c (opencl_language_defn): Initialise new field.
	* p-lang.c (pascal_is_string_type_p): New function.
	(pascal_language_defn): Initialise new field.
	* rust-lang.c (rust_is_string_type_p): New function.
	(rust_language_defn): Initialise new field.
---
 gdb/ChangeLog     | 28 ++++++++++++++++++++++++++++
 gdb/ada-lang.c    |  1 +
 gdb/c-lang.c      | 35 +++++++++++++++++++++++++++++++++++
 gdb/c-lang.h      |  5 +++++
 gdb/d-lang.c      |  1 +
 gdb/f-lang.c      | 11 +++++++++++
 gdb/go-lang.c     | 11 +++++++++++
 gdb/language.c    | 16 ++++++++++++++++
 gdb/language.h    |  7 +++++++
 gdb/m2-lang.c     | 22 ++++++++++++++++++++++
 gdb/objc-lang.c   |  1 +
 gdb/opencl-lang.c |  1 +
 gdb/p-lang.c      | 11 +++++++++++
 gdb/rust-lang.c   | 21 +++++++++++++++++++++
 14 files changed, 171 insertions(+)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index fddb0d7278c..6184067c805 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -14381,6 +14381,7 @@ extern const struct language_defn ada_language_defn = {
   &ada_varobj_ops,
   NULL,
   NULL,
+  ada_is_string_type,
   "(...)"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 3be5ef57252..4d5284e2e80 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -715,6 +715,37 @@ c_watch_location_expression (struct type *type, CORE_ADDR addr)
     (xstrprintf ("* (%s *) %s", name.c_str (), core_addr_to_string (addr)));
 }
 
+/* See c-lang.h.  */
+
+bool
+c_is_string_type_p (struct type *type)
+{
+  type = check_typedef (type);
+  while (TYPE_CODE (type) == TYPE_CODE_REF)
+    {
+      type = TYPE_TARGET_TYPE (type);
+      type = check_typedef (type);
+    }
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_ARRAY:
+      {
+	/* See if target type looks like a string.  */
+	struct type *array_target_type = TYPE_TARGET_TYPE (type);
+	return (TYPE_LENGTH (type) > 0
+		&& TYPE_LENGTH (array_target_type) > 0
+		&& c_textual_element_type (array_target_type, 0));
+      }
+    case TYPE_CODE_STRING:
+      return true;
+    default:
+      break;
+    }
+
+  return false;
+}
+
 \f
 /* Table mapping opcodes into strings for printing operators
    and precedences of the operators.  */
@@ -874,6 +905,7 @@ extern const struct language_defn c_language_defn =
   &c_varobj_ops,
   c_get_compile_context,
   c_compute_program,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
@@ -1019,6 +1051,7 @@ extern const struct language_defn cplus_language_defn =
   &cplus_varobj_ops,
   cplus_get_compile_context,
   cplus_compute_program,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
@@ -1073,6 +1106,7 @@ extern const struct language_defn asm_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
@@ -1127,5 +1161,6 @@ extern const struct language_defn minimal_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
diff --git a/gdb/c-lang.h b/gdb/c-lang.h
index e7b6d5ef737..70a95eadbf8 100644
--- a/gdb/c-lang.h
+++ b/gdb/c-lang.h
@@ -148,6 +148,11 @@ extern int cp_is_vtbl_ptr_type (struct type *);
 
 extern int cp_is_vtbl_member (struct type *);
 
+/* Return true if TYPE is a string type.  Unlike DEFAULT_IS_STRING_TYPE_P
+   this will detect arrays of characters not just TYPE_CODE_STRING.  */
+
+extern bool c_is_string_type_p (struct type *type);
+
 /* These are in c-valprint.c.  */
 
 extern int c_textual_element_type (struct type *, char);
diff --git a/gdb/d-lang.c b/gdb/d-lang.c
index 751c521a75d..0f8f916c9b1 100644
--- a/gdb/d-lang.c
+++ b/gdb/d-lang.c
@@ -251,6 +251,7 @@ extern const struct language_defn d_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/f-lang.c b/gdb/f-lang.c
index 77eb50a0761..b03f1f58b41 100644
--- a/gdb/f-lang.c
+++ b/gdb/f-lang.c
@@ -308,6 +308,16 @@ evaluate_subexp_f (struct type *expect_type, struct expression *exp,
   return nullptr;
 }
 
+/* Return true if TYPE is a string.  */
+static bool
+f_is_string_type_p (struct type *type)
+{
+  type = check_typedef (type);
+  return (TYPE_CODE (type) == TYPE_CODE_STRING
+	  || (TYPE_CODE (type) == TYPE_CODE_ARRAY
+	      && TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_CHAR));
+}
+
 static const char *f_extensions[] =
 {
   ".f", ".F", ".for", ".FOR", ".ftn", ".FTN", ".fpp", ".FPP",
@@ -378,6 +388,7 @@ extern const struct language_defn f_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  f_is_string_type_p,
   "(...)"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/go-lang.c b/gdb/go-lang.c
index b8617cb162d..6473468d4d9 100644
--- a/gdb/go-lang.c
+++ b/gdb/go-lang.c
@@ -130,6 +130,16 @@ go_classify_struct_type (struct type *type)
   return GO_TYPE_NONE;
 }
 
+/* Return true if TYPE is a string.  */
+
+static bool
+go_is_string_type_p (struct type *type)
+{
+  type = check_typedef (type);
+  return (TYPE_CODE (type) == TYPE_CODE_STRUCT
+	  && go_classify_struct_type (type) == GO_TYPE_STRING);
+}
+
 /* Subroutine of unpack_mangled_go_symbol to simplify it.
    Given "[foo.]bar.baz", store "bar" in *PACKAGEP and "baz" in *OBJECTP.
    We stomp on the last '.' to nul-terminate "bar".
@@ -612,6 +622,7 @@ extern const struct language_defn go_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  go_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/language.c b/gdb/language.c
index da8dd1bf7ae..9d0eb03b420 100644
--- a/gdb/language.c
+++ b/gdb/language.c
@@ -723,6 +723,20 @@ default_symbol_name_matcher (const char *symbol_search_name,
 
 /* See language.h.  */
 
+bool
+default_is_string_type_p (struct type *type)
+{
+  type = check_typedef (type);
+  while (TYPE_CODE (type) == TYPE_CODE_REF)
+    {
+      type = TYPE_TARGET_TYPE (type);
+      type = check_typedef (type);
+    }
+  return (TYPE_CODE (type)  == TYPE_CODE_STRING);
+}
+
+/* See language.h.  */
+
 symbol_name_matcher_ftype *
 get_symbol_name_matcher (const language_defn *lang,
 			 const lookup_name_info &lookup_name)
@@ -877,6 +891,7 @@ const struct language_defn unknown_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  default_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
@@ -928,6 +943,7 @@ const struct language_defn auto_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  default_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/language.h b/gdb/language.h
index 261f5a33cc7..e7446efa07f 100644
--- a/gdb/language.h
+++ b/gdb/language.h
@@ -450,6 +450,9 @@ struct language_defn
 				       const struct block *expr_block,
 				       CORE_ADDR expr_pc);
 
+    /* Return true if TYPE is a string type.  */
+    bool (*la_is_string_type_p) (struct type *type);
+
     /* This string is used by the 'set print max-depth' setting.  When GDB
        replaces a struct or union (during value printing) that is "too
        deep" this string is displayed instead.  */
@@ -575,6 +578,10 @@ extern enum language set_language (enum language);
 
 extern int pointer_type (struct type *);
 
+/* Return true if TYPE is a string type, otherwise return false.  This
+   default implementation only detects TYPE_CODE_STRING.  */
+extern bool default_is_string_type_p (struct type *type);
+
 /* Error messages */
 
 extern void range_error (const char *, ...) ATTRIBUTE_PRINTF (1, 2);
diff --git a/gdb/m2-lang.c b/gdb/m2-lang.c
index 9f2a97d54fd..1f7113bd71b 100644
--- a/gdb/m2-lang.c
+++ b/gdb/m2-lang.c
@@ -174,6 +174,27 @@ m2_printstr (struct ui_file *stream, struct type *type, const gdb_byte *string,
     fputs_filtered ("...", stream);
 }
 
+/* Return true if TYPE is a string.  */
+
+static bool
+m2_is_string_type_p (struct type *type)
+{
+  type = check_typedef (type);
+  if (TYPE_CODE (type) == TYPE_CODE_ARRAY
+      && TYPE_LENGTH (type) > 0
+      && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
+    {
+      struct type *elttype = check_typedef (TYPE_TARGET_TYPE (type));
+
+      if (TYPE_LENGTH (elttype) == 1 &&
+	  (TYPE_CODE (elttype) == TYPE_CODE_INT
+	   || TYPE_CODE (elttype) == TYPE_CODE_CHAR))
+	return true;
+    }
+
+  return false;
+}
+
 static struct value *
 evaluate_subexp_modula2 (struct type *expect_type, struct expression *exp,
 			 int *pos, enum noside noside)
@@ -399,6 +420,7 @@ extern const struct language_defn m2_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  m2_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/objc-lang.c b/gdb/objc-lang.c
index af92e55a437..b25a98106c1 100644
--- a/gdb/objc-lang.c
+++ b/gdb/objc-lang.c
@@ -409,6 +409,7 @@ extern const struct language_defn objc_language_defn = {
   &default_varobj_ops,
   NULL,
   NULL,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index c95bf05a98b..93d8e2f5dd3 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -1087,6 +1087,7 @@ extern const struct language_defn opencl_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  c_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
 
diff --git a/gdb/p-lang.c b/gdb/p-lang.c
index 0b85f70dfd2..9b9f19b69cf 100644
--- a/gdb/p-lang.c
+++ b/gdb/p-lang.c
@@ -150,6 +150,16 @@ is_pascal_string_type (struct type *type,int *length_pos,
   return 0;
 }
 
+/* This is a wrapper around IS_PASCAL_STRING_TYPE that returns true if TYPE
+   is a string.  */
+
+static bool
+pascal_is_string_type_p (struct type *type)
+{
+  return is_pascal_string_type (type, nullptr, nullptr, nullptr,
+				nullptr, nullptr) > 0;
+}
+
 static void pascal_one_char (int, struct ui_file *, int *);
 
 /* Print the character C on STREAM as part of the contents of a literal
@@ -460,5 +470,6 @@ extern const struct language_defn pascal_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  pascal_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
index 8faafd49cdd..d878aec6adc 100644
--- a/gdb/rust-lang.c
+++ b/gdb/rust-lang.c
@@ -226,6 +226,26 @@ rust_chartype_p (struct type *type)
 	  && TYPE_UNSIGNED (type));
 }
 
+/* Return true if TYPE is a string type.  */
+static bool
+rust_is_string_type_p (struct type *type)
+{
+  LONGEST low_bound, high_bound;
+
+  type = check_typedef (type);
+  return ((TYPE_CODE (type) == TYPE_CODE_STRING)
+	  || (TYPE_CODE (type) == TYPE_CODE_PTR
+	      && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_ARRAY
+		  && rust_u8_type_p (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)))
+		  && get_array_bounds (TYPE_TARGET_TYPE (type), &low_bound,
+				       &high_bound)))
+	  || ((TYPE_CODE (type) == TYPE_CODE_UNION
+	       || (TYPE_CODE (type) == TYPE_CODE_STRUCT
+		   && !rust_enum_p (type)))
+	      && rust_slice_type_p (type)
+	      && strcmp (TYPE_NAME (type), "&str") == 0));
+}
+
 /* If VALUE represents a trait object pointer, return the underlying
    pointer with the correct (i.e., runtime) type.  Otherwise, return
    NULL.  */
@@ -2142,5 +2162,6 @@ extern const struct language_defn rust_language_defn =
   &default_varobj_ops,
   NULL,
   NULL,
+  rust_is_string_type_p,
   "{...}"			/* la_struct_too_deep_ellipsis */
 };
-- 
2.14.5

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

* [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
                     ` (3 preceding siblings ...)
  2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
@ 2019-04-16 23:06   ` Andrew Burgess
  2019-04-19 14:31     ` Tom Tromey
  2019-04-16 23:06   ` [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool Andrew Burgess
                     ` (2 subsequent siblings)
  7 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

We currently add the -fno-stack-protector option to the compiler flags
when compiling rust tests, as this is not a valid rustcc flag, this
means non of the test tests will compile.

gdb/testsuite/ChangeLog:

	* lib/gdb.exp (gdb_compile): Don't add -fno-stack-protector option
	if we're using rustcc.
---
 gdb/testsuite/ChangeLog   | 5 +++++
 gdb/testsuite/lib/gdb.exp | 6 +++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 1176fdded14..6c0f243eef8 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3566,7 +3566,11 @@ proc gdb_compile {source dest type options} {
 	 && !([test_compiler_info {gcc-[0-3]-*}]
 	      || [test_compiler_info {gcc-4-0-*}]) } {
         # Put it at the front to not override any user-provided value.
-        lappend new_options "early_flags=-fno-stack-protector"
+	if {[lsearch -exact $options rust] != -1} {
+	    # -fno-stack-protector is not a rustcc option.
+	} else {
+	    lappend new_options "early_flags=-fno-stack-protector"
+	}
     }
 
     # Because we link with libraries using their basename, we may need
-- 
2.14.5

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

* [PATCHv2 0/5] Add new 'print max-depth' feature
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
@ 2019-04-16 23:06 Andrew Burgess
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
  2019-04-29 21:14 ` [PATCHv2 0/5] Add new 'print max-depth' feature Andrew Burgess
  7 siblings, 2 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This revision hopefully addresses all of the feedback from the first
version.

Patches #1 and #2 are new, minor clean up patches.

Patch #3 (previously #1) is unchanged.

Patch #4 is new, this adds a new predicate to the language structure
to decide if a given type is a string or not.

Patch #5 this is updated from the previous version, there's more
documentation, many more tests, minor code issues identified in v1
have been addressed.

--

Andrew Burgess (5):
  gdb/ada: Update some predicate functions to return bool
  gdb/testsuite: Don't add gcc flags when compiling rust tests
  gdb: Introduce new language field la_struct_too_deep_ellipsis
  gdb: Introduce new language field la_is_string_type_p
  gdb: Introduce 'print max-depth' feature

 gdb/ChangeLog                                 |  85 +++++++++
 gdb/NEWS                                      |   9 +
 gdb/ada-lang.c                                |  12 +-
 gdb/ada-lang.h                                |   4 +-
 gdb/c-lang.c                                  |  47 ++++-
 gdb/c-lang.h                                  |   5 +
 gdb/cp-valprint.c                             |  80 +++++----
 gdb/d-lang.c                                  |   4 +-
 gdb/doc/ChangeLog                             |   8 +
 gdb/doc/gdb.texinfo                           |  70 ++++++++
 gdb/doc/guile.texi                            |   3 +
 gdb/doc/python.texi                           |   8 +
 gdb/f-lang.c                                  |  14 +-
 gdb/go-lang.c                                 |  14 +-
 gdb/guile/scm-pretty-print.c                  |  19 +-
 gdb/language.c                                |  22 ++-
 gdb/language.h                                |  13 ++
 gdb/m2-lang.c                                 |  25 ++-
 gdb/objc-lang.c                               |   4 +-
 gdb/opencl-lang.c                             |   4 +-
 gdb/p-lang.c                                  |  14 +-
 gdb/python/py-prettyprint.c                   |  15 +-
 gdb/python/py-value.c                         |   7 +-
 gdb/rust-lang.c                               |  24 ++-
 gdb/testsuite/ChangeLog                       |  23 +++
 gdb/testsuite/gdb.base/max-depth.c            | 232 +++++++++++++++++++++++++
 gdb/testsuite/gdb.base/max-depth.exp          | 165 ++++++++++++++++++
 gdb/testsuite/gdb.fortran/max-depth.exp       |  41 +++++
 gdb/testsuite/gdb.fortran/max-depth.f90       |  42 +++++
 gdb/testsuite/gdb.go/max-depth.exp            |  40 +++++
 gdb/testsuite/gdb.go/max-depth.go             |  41 +++++
 gdb/testsuite/gdb.modula2/max-depth.c         |  45 +++++
 gdb/testsuite/gdb.modula2/max-depth.exp       |  37 ++++
 gdb/testsuite/gdb.python/py-format-string.exp |  21 +++
 gdb/testsuite/gdb.python/py-nested-maps.c     | 130 ++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.exp   | 238 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.py    | 135 +++++++++++++++
 gdb/testsuite/lib/gdb.exp                     |  36 +++-
 gdb/valprint.c                                |  56 +++++-
 gdb/valprint.h                                |  12 ++
 40 files changed, 1746 insertions(+), 58 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/max-depth.c
 create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
 create mode 100644 gdb/testsuite/gdb.go/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.go/max-depth.go
 create mode 100644 gdb/testsuite/gdb.modula2/max-depth.c
 create mode 100644 gdb/testsuite/gdb.modula2/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py

-- 
2.14.5

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

* [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
                     ` (4 preceding siblings ...)
  2019-04-16 23:06   ` [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests Andrew Burgess
@ 2019-04-16 23:06   ` Andrew Burgess
  2019-04-18  0:03     ` Joel Brobecker
  2019-04-16 23:06   ` [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
  2019-04-16 23:06   ` [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p Andrew Burgess
  7 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

A later commit would like to make use of a pointer to the function
ada_is_string_type, however, this will require the function to return
a bool (so the signature matches).

As the ada_is_string_type is a predicate function, and its return
value is only ever used as either true or false, then this commit
updates the function to return a bool.

As a consequence ada_is_character_type needs to change too.

There should be no user visible changes after this commit.

gdb/ChangeLog:

	* ada-lang.c (ada_is_character_type): Change return type to bool.
	(ada_is_string_type): Likewise.
	* ada-lang.h (ada_is_character_type): Update declaration
	(ada_is_string_type): Likewise.
---
 gdb/ChangeLog  | 7 +++++++
 gdb/ada-lang.c | 8 ++++----
 gdb/ada-lang.h | 4 ++--
 3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index ccf8ed8039e..622cf59de68 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -9396,7 +9396,7 @@ value_val_atr (struct type *type, struct value *arg)
    [At the moment, this is true only for Character and Wide_Character;
    It is a heuristic test that could stand improvement].  */
 
-int
+bool
 ada_is_character_type (struct type *type)
 {
   const char *name;
@@ -9404,7 +9404,7 @@ ada_is_character_type (struct type *type)
   /* If the type code says it's a character, then assume it really is,
      and don't check any further.  */
   if (TYPE_CODE (type) == TYPE_CODE_CHAR)
-    return 1;
+    return true;
   
   /* Otherwise, assume it's a character type iff it is a discrete type
      with a known character type name.  */
@@ -9420,7 +9420,7 @@ ada_is_character_type (struct type *type)
 
 /* True if TYPE appears to be an Ada string type.  */
 
-int
+bool
 ada_is_string_type (struct type *type)
 {
   type = ada_check_typedef (type);
@@ -9435,7 +9435,7 @@ ada_is_string_type (struct type *type)
       return ada_is_character_type (elttype);
     }
   else
-    return 0;
+    return false;
 }
 
 /* The compiler sometimes provides a parallel XVS type for a given
diff --git a/gdb/ada-lang.h b/gdb/ada-lang.h
index ee03dbd2aad..8740916a193 100644
--- a/gdb/ada-lang.h
+++ b/gdb/ada-lang.h
@@ -268,9 +268,9 @@ extern struct value *ada_value_primitive_packed_val (struct value *,
 
 extern struct type *ada_coerce_to_simple_array_type (struct type *);
 
-extern int ada_is_character_type (struct type *);
+extern bool ada_is_character_type (struct type *);
 
-extern int ada_is_string_type (struct type *);
+extern bool ada_is_string_type (struct type *);
 
 extern int ada_is_tagged_type (struct type *, int);
 
-- 
2.14.5

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

* [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
                     ` (2 preceding siblings ...)
  2019-03-28 12:47   ` [PATCH 0/2] Add new " Pedro Alves
@ 2019-04-16 23:06   ` Andrew Burgess
  2019-04-17  7:42     ` Philippe Waroquiers
                       ` (2 more replies)
  2019-04-16 23:06   ` [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests Andrew Burgess
                     ` (3 subsequent siblings)
  7 siblings, 3 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-04-16 23:06 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Introduce a new print setting max-depth which can be set with 'set
print max-depth DEPTH'.  The default value of DEPTH is 20, but this
can also be set to unlimited.

When GDB is printing a value containing nested structures GDB will
stop descending at depth DEPTH.  Here is a small example:

    typedef struct s1 { int a; } s1;
    typedef struct s2 { s1 b; } s2;
    typedef struct s3 { s2 c; } s3;
    typedef struct s4 { s3 d; } s4;

    s4 var = { { { { 3 } } } };

The following table shows how various depth settings affect printing
of 'var':

    | Depth Setting | Result of 'p var'              |
    |---------------+--------------------------------|
    |     Unlimited | $1 = {d = {c = {b = {a = 3}}}} |
    |             4 | $1 = {d = {c = {b = {a = 3}}}} |
    |             3 | $1 = {d = {c = {b = {...}}}}   |
    |             2 | $1 = {d = {c = {...}}}         |
    |             1 | $1 = {d = {...}}               |
    |             0 | $1 = {...}                     |

Only structures, unions, and arrays are replaced in this way, scalars
and strings are not replaced.

The replacement is counted from the level at which you print, not from
the top level of the structure.  So, consider the above example and
this GDB session:

    (gdb) set print max-depth 2
    (gdb) p var
    $1 = {d = {c = {...}}}
    (gdb) p var.d
    $2 = {c = {b = {...}}}
    (gdb) p var.d.c
    $3 = {b = {a = 3}}

Setting the max-depth to 2 doesn't prevent the user from exploring
deeper into 'var' by asking for specific sub-fields to be printed.

The motivation behind this feature is to try and give the user more
control over how much is printed when examining large, complex data
structures.

The default max-depth of 20 means that there is a change in GDB's
default behaviour.  Someone printing a data structure with 20 levels
of nesting will now see '{...}' instead of their data, they would need
to adjust the max depth, or call print again naming a specific field
in order to dig deeper into their data structure.  If this is
considered a problem then we could increase the default, or even make
the default unlimited.

This commit relies on the previous commit, which added a new field to
the language structure, this new field was a string that contained the
pattern that should be used when a structure/union/array is replaced
in the output, this allows languages to use a syntax that is more
appropriate, mostly this will be selecting the correct types of
bracket '(...)' or '{...}', both of which are currently in use.

This commit should have no impact on MI output, expressions are
printed through the MI using -var-create and then -var-list-children.
As each use of -var-list-children only ever displays a single level of
an expression then the max-depth setting will have no impact.

This commit also adds the max-depth mechanism to the scripting
language pretty printers following basically the same rules as for the
built in value printing.

One quirk is that when printing a value using the display hint 'map',
if the keys of the map are structs then GDB will hide the keys one
depth level after it hides the values, this ensures that GDB produces
output like this:

  $1 = map_object = {[{key1}] = {...}, [{key2}] = {...}}

Instead of this less helpful output:

  $1 = map_object = {[{...}] = {...}, [{...}] = {...}}

This is covered by the new tests in gdb.python/py-nested-maps.exp.

gdb/ChangeLog:

	* cp-valprint.c (cp_print_value_fields): Allow an additional level
	of depth when printing anonymous structs or unions.
	* guile/scm-pretty-print.c (gdbscm_apply_val_pretty_printer):
	Don't print either the top-level value, or the children if the
	max-depth is exceeded.
	(ppscm_print_children): When printing the key of a map, allow one
	extra level of depth.
	* python/py-prettyprint.c (gdbpy_apply_val_pretty_printer): Don't
	print either the top-level value, or the children if the max-depth
	is exceeded.
	(print_children): When printing the key of a map, allow one extra
	level of depth.
	* python/py-value.c (valpy_format_string): Add max_depth keyword.
	* valprint.c: (PRINT_MAX_DEPTH_DEFAULT): Define.
	(user_print_options): Initialise max_depth field.
	(val_print_scalar_or_string_type_p): New function.
	(val_print): Check to see if the max depth has been reached.
	(val_print_check_max_depth): Define new function.
	(show_print_max_depth): New function.
	(_initialize_valprint): Add 'print max-depth' option.
	* valprint.h (struct value_print_options) <max_depth>: New field.
	(val_print_check_max_depth): Declare new function.
	* NEWS: Document new feature.

gdb/doc/ChangeLog:

	* gdb.texinfo (Print Settings): Document 'print max-depth'.
	* guile.texi (Guile Pretty Printing API): Document that 'print
	max-depth' can effect the display of a values children.
	* python.texi (Pretty Printing API): Likewise.
	(Values From Inferior): Document max_depth keyword.

gdb/testsuite/ChangeLog:

	* gdb.base/max-depth.c: New file.
	* gdb.base/max-depth.exp: New file.
	* gdb.python/py-nested-maps.c: New file.
	* gdb.python/py-nested-maps.exp: New file.
	* gdb.python/py-nested-maps.py: New file.
	* gdb.python/py-format-string.exp (test_max_depth): New proc.
	(test_all_common): Call test_max_depth.
	* gdb.fortran/max-depth.exp: New file.
	* gdb.fortran/max-depth.f90: New file.
	* gdb.go/max-depth.exp: New file.
	* gdb.go/max-depth.go: New file.
	* gdb.modula2/max-depth.exp: New file.
	* gdb.modula2/max-depth.c: New file.
	* lib/gdb.exp (get_print_expr_at_depths): New proc.
---
 gdb/ChangeLog                                 |  30 ++++
 gdb/NEWS                                      |   9 +
 gdb/cp-valprint.c                             |  80 +++++----
 gdb/doc/ChangeLog                             |   8 +
 gdb/doc/gdb.texinfo                           |  70 ++++++++
 gdb/doc/guile.texi                            |   3 +
 gdb/doc/python.texi                           |   8 +
 gdb/guile/scm-pretty-print.c                  |  19 +-
 gdb/python/py-prettyprint.c                   |  15 +-
 gdb/python/py-value.c                         |   7 +-
 gdb/testsuite/ChangeLog                       |  18 ++
 gdb/testsuite/gdb.base/max-depth.c            | 232 +++++++++++++++++++++++++
 gdb/testsuite/gdb.base/max-depth.exp          | 165 ++++++++++++++++++
 gdb/testsuite/gdb.fortran/max-depth.exp       |  41 +++++
 gdb/testsuite/gdb.fortran/max-depth.f90       |  42 +++++
 gdb/testsuite/gdb.go/max-depth.exp            |  40 +++++
 gdb/testsuite/gdb.go/max-depth.go             |  41 +++++
 gdb/testsuite/gdb.modula2/max-depth.c         |  45 +++++
 gdb/testsuite/gdb.modula2/max-depth.exp       |  37 ++++
 gdb/testsuite/gdb.python/py-format-string.exp |  21 +++
 gdb/testsuite/gdb.python/py-nested-maps.c     | 130 ++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.exp   | 238 ++++++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-nested-maps.py    | 135 +++++++++++++++
 gdb/testsuite/lib/gdb.exp                     |  30 ++++
 gdb/valprint.c                                |  56 +++++-
 gdb/valprint.h                                |  12 ++
 26 files changed, 1496 insertions(+), 36 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/max-depth.c
 create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
 create mode 100644 gdb/testsuite/gdb.go/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.go/max-depth.go
 create mode 100644 gdb/testsuite/gdb.modula2/max-depth.c
 create mode 100644 gdb/testsuite/gdb.modula2/max-depth.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
 create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py

diff --git a/gdb/NEWS b/gdb/NEWS
index 5309a8f923b..26bebf7b48c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -19,6 +19,15 @@
 * Two new convernience functions $_cimag and $_creal that extract the
   imaginary and real parts respectively from complex numbers.
 
+* New commands
+
+set print max-depth
+show print max-depth
+  Allows deeply nested structures to be simplified when printing by
+  replacing deeply nested parts (beyond the max-depth) with ellipses.
+  The default max-depth is 20, but this can be set to unlimited to get
+  the old behavior back.
+
 * Python API
 
   ** The gdb.Value type has a new method 'format_string' which returns a
diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
index e883179dfad..ff860df499a 100644
--- a/gdb/cp-valprint.c
+++ b/gdb/cp-valprint.c
@@ -272,10 +272,23 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 				   current_language->la_language,
 				   DMGL_PARAMS | DMGL_ANSI);
 	  annotate_field_name_end ();
+
+	  /* We tweak various options in a few cases below.  */
+	  value_print_options options_copy = *options;
+	  value_print_options *opts = &options_copy;
+
 	  /* Do not print leading '=' in case of anonymous
 	     unions.  */
 	  if (strcmp (TYPE_FIELD_NAME (type, i), ""))
 	    fputs_filtered (" = ", stream);
+	  else
+	    {
+	      /* If this is an anonymous field then we want to consider it
+		 as though it is at its parent's depth when it comes to the
+		 max print depth.  */
+	      if (opts->max_depth != -1 && opts->max_depth < INT_MAX)
+		++opts->max_depth;
+	    }
 	  annotate_field_value ();
 
 	  if (!field_is_static (&TYPE_FIELD (type, i))
@@ -299,14 +312,12 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		}
 	      else
 		{
-		  struct value_print_options opts = *options;
-
-		  opts.deref_ref = 0;
+		  opts->deref_ref = 0;
 
 		  v = value_field_bitfield (type, i, valaddr, offset, val);
 
-		  common_val_print (v, stream, recurse + 1, &opts,
-				    current_language);
+		  common_val_print (v, stream, recurse + 1,
+				    opts, current_language);
 		}
 	    }
 	  else
@@ -333,8 +344,7 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		    }
 
 		  cp_print_static_field (TYPE_FIELD_TYPE (type, i),
-					 v, stream, recurse + 1,
-					 options);
+					 v, stream, recurse + 1, opts);
 		}
 	      else if (i == vptr_fieldno && type == vptr_basetype)
 		{
@@ -346,20 +356,18 @@ cp_print_value_fields (struct type *type, struct type *real_type,
 		      CORE_ADDR addr;
 		      
 		      addr = extract_typed_address (valaddr + i_offset, i_type);
-		      print_function_pointer_address (options,
+		      print_function_pointer_address (opts,
 						      get_type_arch (type),
 						      addr, stream);
 		    }
 		}
 	      else
 		{
-		  struct value_print_options opts = *options;
-
-		  opts.deref_ref = 0;
+		  opts->deref_ref = 0;
 		  val_print (TYPE_FIELD_TYPE (type, i),
 			     offset + TYPE_FIELD_BITPOS (type, i) / 8,
 			     address,
-			     stream, recurse + 1, val, &opts,
+			     stream, recurse + 1, val, opts,
 			     current_language);
 		}
 	    }
@@ -575,25 +583,35 @@ cp_print_value (struct type *type, struct type *real_type,
 	{
 	  int result = 0;
 
-	  /* Attempt to run an extension language pretty-printer on the
-	     baseclass if possible.  */
-	  if (!options->raw)
-	    result
-	      = apply_ext_lang_val_pretty_printer (baseclass,
-						   thisoffset + boffset,
-						   value_address (base_val),
-						   stream, recurse,
-						   base_val, options,
-						   current_language);
-
-	  if (!result)
-	    cp_print_value_fields (baseclass, thistype,
-				   thisoffset + boffset,
-				   value_address (base_val),
-				   stream, recurse, base_val, options,
-				   ((struct type **)
-				    obstack_base (&dont_print_vb_obstack)),
-				   0);
+	  if (options->max_depth > -1
+	      && recurse >= options->max_depth)
+	    {
+	      const struct language_defn *language = current_language;
+	      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);
+	      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
+	    }
+	  else
+	    {
+	      /* Attempt to run an extension language pretty-printer on the
+		 baseclass if possible.  */
+	      if (!options->raw)
+		result
+		  = apply_ext_lang_val_pretty_printer (baseclass,
+						       thisoffset + boffset,
+						       value_address (base_val),
+						       stream, recurse,
+						       base_val, options,
+						       current_language);
+
+	      if (!result)
+		cp_print_value_fields (baseclass, thistype,
+				       thisoffset + boffset,
+				       value_address (base_val),
+				       stream, recurse, base_val, options,
+				       ((struct type **)
+					obstack_base (&dont_print_vb_obstack)),
+				       0);
+	    }
 	}
       fputs_filtered (", ", stream);
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f410d026b82..8c0661d7ca8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -10560,6 +10560,76 @@
 Display the current threshold for printing repeated identical
 elements.
 
+@item set print max-depth @var{depth}
+@item set print max-depth unlimited
+@cindex printing nested structures
+Set the threshold after which nested structures are replaced with
+ellipsis, this can make visualising deeply nested structures easier.
+
+For example, given this C code
+
+@smallexample
+typedef struct s1 @{ int a; @} s1;
+typedef struct s2 @{ s1 b; @} s2;
+typedef struct s3 @{ s2 c; @} s3;
+typedef struct s4 @{ s3 d; @} s4;
+
+s4 var = @{ @{ @{ @{ 3 @} @} @} @};
+@end smallexample
+
+@noindent
+And with @code{set print max-depth unlimited} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{c = @{b = @{a = 3@}@}@}@}
+@end smallexample
+
+@noindent
+With @code{set print max-depth 0} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{...@}
+@end smallexample
+
+@noindent
+With @code{set print max-depth 1} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{...@}@}
+@end smallexample
+
+@noindent
+With @code{set print max-depth 2} in effect @samp{p var}
+would print
+
+@smallexample
+$1 = @{d = @{c = @{...@}@}@}
+@end smallexample
+
+To see the contents of structures that have been hidden the user can
+either increase the print max-depth to increase, or they can print
+elements of structures that are visible, for example
+
+@smallexample
+(gdb) p var
+$1 = @{d = @{c = @{...@}@}@}
+(gdb) p var.d
+$2 = @{c = @{b = @{...@}@}@}
+(gdb) p var.d.c
+$3 = @{b = @{a = 3@}@}
+@end smallexample
+
+The pattern used to replace nested structures varies based on
+language, for most languages @code{@{...@}} is used, but Fortran uses
+@code{(...)}.
+
+@item show print max-depth
+Display the current threshold after which nested structures are
+replaces with ellipsis.
+
 @item set print null-stop
 @cindex @sc{null} elements in arrays
 Cause @value{GDBN} to stop printing the characters of an array when the first
diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
index 721338b050c..f4c29dc3df8 100644
--- a/gdb/doc/guile.texi
+++ b/gdb/doc/guile.texi
@@ -1469,6 +1469,9 @@
 
 If @var{children} is @code{#f}, @value{GDBN} will act
 as though the value has no children.
+
+Children may be hidden from display based on the value of @samp{set
+print max-depth} (@pxref{Print Settings}).
 @end table
 @end deffn
 
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 36947de30b0..b47c38deef7 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -931,6 +931,11 @@
 number of elements (see @code{set print elements} in @ref{Print
 Settings}).
 
+@item max_depth
+The maximum depth to print for nested structs and unions, or @code{-1}
+to print an unlimited number of elements (see @code{set print
+max-depth} 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
@@ -1365,6 +1370,9 @@
 
 This method is optional.  If it does not exist, @value{GDBN} will act
 as though the value has no children.
+
+Children may be hidden from display based on the value of @samp{set
+print max-depth} (@pxref{Print Settings}).
 @end defun
 
 @defun pretty_printer.display_hint (self)
diff --git a/gdb/guile/scm-pretty-print.c b/gdb/guile/scm-pretty-print.c
index 656c4bbc52e..630a062148e 100644
--- a/gdb/guile/scm-pretty-print.c
+++ b/gdb/guile/scm-pretty-print.c
@@ -897,7 +897,18 @@ ppscm_print_children (SCM printer, enum display_hint hint,
 	      ppscm_print_exception_unless_memory_error (except_scm, stream);
 	      break;
 	    }
-	  common_val_print (value, stream, recurse + 1, options, language);
+	  else
+	    {
+	      /* When printing the key of a map we allow one additional
+		 level of depth.  This means the key will print before the
+		 value does.  */
+	      struct value_print_options opt = *options;
+	      if (is_map && i % 2 == 0
+		  && opt.max_depth != -1
+		  && opt.max_depth < INT_MAX)
+		++opt.max_depth;
+	      common_val_print (value, stream, recurse + 1, &opt, language);
+	    }
 	}
 
       if (is_map && i % 2 == 0)
@@ -984,6 +995,12 @@ gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang,
     }
   gdb_assert (ppscm_is_pretty_printer_worker (printer));
 
+  if (val_print_check_max_depth (stream, recurse, options, language))
+    {
+      result = EXT_LANG_RC_OK;
+      goto done;
+    }
+
   /* If we are printing a map, we want some special formatting.  */
   hint = ppscm_get_display_hint_enum (printer);
   if (hint == HINT_ERROR)
diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index 7760cab0ae0..fdc520d8443 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -517,7 +517,17 @@ print_children (PyObject *printer, const char *hint,
 	      error (_("Error while executing Python code."));
 	    }
 	  else
-	    common_val_print (value, stream, recurse + 1, options, language);
+	    {
+	      /* When printing the key of a map we allow one additional
+		 level of depth.  This means the key will print before the
+		 value does.  */
+	      struct value_print_options opt = *options;
+	      if (is_map && i % 2 == 0
+		  && opt.max_depth != -1
+		  && opt.max_depth < INT_MAX)
+		++opt.max_depth;
+	      common_val_print (value, stream, recurse + 1, &opt, language);
+	    }
 	}
 
       if (is_map && i % 2 == 0)
@@ -590,6 +600,9 @@ gdbpy_apply_val_pretty_printer (const struct extension_language_defn *extlang,
   if (printer == Py_None)
     return EXT_LANG_RC_NOP;
 
+  if (val_print_check_max_depth (stream, recurse, options, language))
+    return EXT_LANG_RC_OK;
+
   /* If we are printing a map, we want some special formatting.  */
   gdb::unique_xmalloc_ptr<char> hint (gdbpy_get_display_hint (printer.get ()));
 
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index d3f4de40540..cbc30aa8c3e 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -623,6 +623,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
       "static_members",		/* See set print static-members on|off.  */
       /* C non-bool options.  */
       "max_elements", 		/* See set print elements N.  */
+      "max_depth",		/* See set print max-depth N.  */
       "repeat_threshold",	/* See set print repeats.  */
       "format",			/* The format passed to the print command.  */
       NULL
@@ -665,7 +666,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
   char *format = NULL;
   if (!gdb_PyArg_ParseTupleAndKeywords (args,
 					kw,
-					"|O!O!O!O!O!O!O!O!O!IIs",
+					"|O!O!O!O!O!O!O!O!O!IIIs",
 					keywords,
 					&PyBool_Type, &raw_obj,
 					&PyBool_Type, &pretty_arrays_obj,
@@ -677,6 +678,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
 					&PyBool_Type, &actual_objects_obj,
 					&PyBool_Type, &static_members_obj,
 					&opts.print_max,
+					&opts.max_depth,
 					&opts.repeat_count_threshold,
 					&format))
     return NULL;
@@ -702,7 +704,8 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
     return NULL;
 
   /* Numeric arguments for which 0 means unlimited (which we represent as
-     UINT_MAX).  */
+     UINT_MAX).  Note that the max-depth numeric argument uses -1 as
+     unlimited, and 0 is a valid choice.  */
   if (opts.print_max == 0)
     opts.print_max = UINT_MAX;
   if (opts.repeat_count_threshold == 0)
diff --git a/gdb/testsuite/gdb.base/max-depth.c b/gdb/testsuite/gdb.base/max-depth.c
new file mode 100644
index 00000000000..3c57249e699
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-depth.c
@@ -0,0 +1,232 @@
+/* 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/>.  */
+
+struct s1
+{
+  int x;
+  int y;
+} s1;
+
+struct s2
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+  };
+} s2;
+
+struct s3
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  };
+} s3;
+
+struct s4
+{
+  int x;
+  int y;
+  struct
+  {
+    int x;
+    int y;
+    struct
+    {
+      int x;
+      int y;
+    } l2;
+  } l1;
+} s4;
+
+struct s5
+{
+  union
+  {
+    int raw[3];
+    struct
+    {
+      int x;
+      int y;
+      int z;
+    };
+  };
+} s5;
+
+typedef struct
+{
+  union
+  {
+    int raw[3];
+    struct
+    {
+      int x;
+      int y;
+      int z;
+    };
+  };
+} s6_t;
+
+s6_t s6;
+
+struct s7
+{
+  struct
+  {
+    int x;
+    int y;
+  };
+  struct
+  {
+    int z;
+    int a;
+  };
+  struct
+  {
+    int b;
+    int c;
+  };
+} s7;
+
+struct s8
+{
+  int x;
+  int y;
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  } d1;
+} s8;
+
+struct s9
+{
+  int x;
+  int y;
+  struct
+  {
+    int k;
+    int j;
+    struct
+    {
+      int z;
+      int a;
+      struct
+      {
+        int b;
+        int c;
+      };
+    } d1;
+  };
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b;
+      int c;
+    };
+  } d2;
+} s9;
+
+struct s10
+{
+  int x[10];
+  int y;
+  struct
+  {
+    int k[10];
+    int j;
+    struct
+    {
+      int z;
+      int a;
+      struct
+      {
+        int b[10];
+        int c;
+      };
+    } d1;
+  };
+  struct
+  {
+    int z;
+    int a;
+    struct
+    {
+      int b[10];
+      int c;
+    };
+  } d2;
+} s10;
+
+struct s11
+{
+  int x;
+  char s[10];
+  struct
+  {
+    int z;
+    int a;
+  };
+} s11;
+
+/* The following are C++ inheritance testing.  */
+#ifdef __cplusplus
+
+/* This is non-virtual inheritance.  */
+struct C1 { int c1 = 1; } c1;
+struct C2 { int c2 = 2; } c2;
+struct C3 : C2 { int c3 = 3; } c3;
+struct C4 { int c4 = 4; } c4;
+struct C5 : C4 { int c5 = 5; } c5;
+struct C6 : C5 { int c6 = 6; } c6;
+struct C7 : C1, C3, C6 { int c7 = 7; } c7;
+
+/* This is virtual inheritance.  */
+struct V1 { int v1 = 1; } v1;
+struct V2 : virtual V1 { int v2 = 2; } v2;
+struct V3 : virtual V1 { int v3 = 3; } v3;
+struct V4 : virtual V2 { int v4 = 4; } v4;
+struct V5 : virtual V2 { int v5 = 1; } v5;
+struct V6 : virtual V2, virtual V3 { int v6 = 1; } v6;
+struct V7 : virtual V4, virtual V5, virtual V6 { int v7 = 1; } v7;
+
+#endif /* __cplusplus */
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/max-depth.exp b/gdb/testsuite/gdb.base/max-depth.exp
new file mode 100644
index 00000000000..b3b78762eb1
--- /dev/null
+++ b/gdb/testsuite/gdb.base/max-depth.exp
@@ -0,0 +1,165 @@
+# 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/>.
+
+# Tests GDB's handling of 'set print max-depth'.
+
+# Only test C++ if we are able.  Always use C.
+if { [skip_cplus_tests] || [get_compiler_info "c++"] } {
+    set lang {c}
+} else {
+    set lang {c c++}
+}
+
+foreach l $lang {
+    set dir "$l"
+    remote_exec host "rm -rf [standard_output_file ${dir}]"
+    remote_exec host "mkdir -p [standard_output_file ${dir}]"
+}
+
+proc compile_and_run_tests { lang } {
+    global testfile
+    global srcfile
+    global binfile
+    global hex
+
+    standard_testfile .c
+
+    # Create the additional flags.
+    set flags "debug"
+    lappend flags $lang
+
+    set dir "$lang"
+    set binfile [standard_output_file ${dir}/${testfile}]
+    if { [prepare_for_testing "failed to prepare" "${binfile}" "${srcfile}" "${flags}"] } {
+	return 0
+    }
+
+    # Advance to main.
+    if { ![runto_main] } then {
+	fail "can't run to main"
+	return 0
+    }
+
+    # The max-depth setting has no effect as the anonymous scopes are
+    # ignored and the members are aggregated into the parent scope.
+    gdb_print_expr_at_depths "s1" {"{...}" \
+				       "{x = 0, y = 0}"\
+				       "{x = 0, y = 0}"}
+
+    gdb_print_expr_at_depths "s2" {"{...}" \
+				       "{x = 0, y = 0, {z = 0, a = 0}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0}}"}
+
+    gdb_print_expr_at_depths "s3" {"{...}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
+				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Increasing max-depth unfurls more of the object.
+    gdb_print_expr_at_depths "s4" {"{...}" \
+				       "{x = 0, y = 0, l1 = {...}}" \
+				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {...}}}" \
+				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {x = 0, y = 0}}}"}
+
+    # Check handling of unions, in this case 'raw' is printed instead of
+    # just {...} as this is not useful.
+    gdb_print_expr_at_depths "s5" {"{...}" \
+				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
+				       "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"}
+
+    # Check handling of typedefs.
+    gdb_print_expr_at_depths "s6" {"{...}" \
+				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
+				       "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"}
+
+    # Multiple anonymous structures in parallel.
+    gdb_print_expr_at_depths "s7" {"{...}" \
+				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}" \
+				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}"}
+
+    # Flip flop between named and anonymous.  Expected to unfurl to the
+    # first non-anonymous type.
+    gdb_print_expr_at_depths "s8" {"{...}" \
+				       "{x = 0, y = 0, d1 = {...}}" \
+				       "{x = 0, y = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Imbalanced tree, this will unfurl one size more than the other as
+    # one side has more anonymous levels.
+    gdb_print_expr_at_depths "s9" {"{...}" \
+				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {...}}, d2 = {...}}" \
+				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}, d2 = {z = 0, a = 0, {b = 0, c = 0}}}"}
+
+    # Arrays are treated as an extra level, while scalars are not.
+    gdb_print_expr_at_depths "s10" {"{...}" \
+					"{x = {...}, y = 0, {k = {...}, j = 0, d1 = {...}}, d2 = {...}}" \
+					"{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = {...}, c = 0}}}, d2 = {z = 0, a = 0, {b = {...}, c = 0}}}" \
+					"{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}, d2 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}"}
+
+    # Strings are treated as scalars.
+    gdb_print_expr_at_depths "s11" {"{...}" \
+					"{x = 0, s = \"\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\", {z = 0, a = 0}}"}
+
+
+    if { $lang == "c++" } {
+	gdb_print_expr_at_depths "c1" {"{...}" \
+					   "{c1 = 1}" }
+	gdb_print_expr_at_depths "c2" { "{...}" "{c2 = 2}" }
+	gdb_print_expr_at_depths "c3" { "{...}" \
+					    "{<C2> = {...}, c3 = 3}" \
+					    "{<C2> = {c2 = 2}, c3 = 3}" }
+	gdb_print_expr_at_depths "c4" { "{...}" "{c4 = 4}" }
+	gdb_print_expr_at_depths "c5" { "{...}" \
+					    "{<C4> = {...}, c5 = 5}" \
+					    "{<C4> = {c4 = 4}, c5 = 5}" }
+	gdb_print_expr_at_depths "c6" { "{...}" \
+					    "{<C5> = {...}, c6 = 6}" \
+					    "{<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}" \
+					    "{<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}" }
+	gdb_print_expr_at_depths "c7" { "{...}" \
+					    "{<C1> = {...}, <C3> = {...}, <C6> = {...}, c7 = 7}" \
+					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {...}, c3 = 3}, <C6> = {<C5> = {...}, c6 = 6}, c7 = 7}" \
+					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}, c7 = 7}" \
+					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}, c7 = 7}" }
+
+	gdb_print_expr_at_depths "v1" [list "{...}" "{v1 = 1}" ]
+	gdb_print_expr_at_depths "v2" [list "{...}" \
+					    "{<V1> = {...}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" \
+					    "{<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" ]
+	gdb_print_expr_at_depths "v3" [list "{...}" \
+					    "{<V1> = {...}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" \
+					    "{<V1> = {v1 = 1}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" ]
+	gdb_print_expr_at_depths "v4" [list "{...}" \
+					    "{<V2> = {...}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \
+					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \
+					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" ]
+	gdb_print_expr_at_depths "v5" [list "{...}" \
+					    "{<V2> = {...}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \
+					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \
+					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" ]
+	gdb_print_expr_at_depths "v6" [list "{...}" \
+					    "{<V2> = {...}, <V3> = {...}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \
+					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \
+					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" ]
+	gdb_print_expr_at_depths "v7" [list "{...}" \
+					    "{<V4> = {...}, <V5> = {...}, <V6> = {...}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
+					    "{<V4> = {<V2> = {...}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {...}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
+					    "{<V4> = {<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
+					    "{<V4> = {<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" ]
+    }
+}
+
+foreach_with_prefix l $lang {
+    compile_and_run_tests $l
+}
diff --git a/gdb/testsuite/gdb.fortran/max-depth.exp b/gdb/testsuite/gdb.fortran/max-depth.exp
new file mode 100644
index 00000000000..8b0bb8058fe
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/max-depth.exp
@@ -0,0 +1,41 @@
+# 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/>.
+
+# This file tests GDB's handling of 'set print max-depth' for nested
+# fortran types.
+
+load_lib "fortran.exp"
+
+if { [skip_fortran_tests] } { continue }
+
+standard_testfile .f90
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug f90}] } {
+    return -1
+}
+
+if { ![runto MAIN__] } {
+    perror "Could not run to breakpoint `MAIN__'."
+    continue
+}
+
+gdb_breakpoint [gdb_get_line_number "stop-here"]
+gdb_continue_to_breakpoint "stop-here" ".*stop-here.*"
+
+gdb_print_expr_at_depths "var" {"\\(\\.\\.\\.\\)" \
+				    "\\( d = \\(\\.\\.\\.\\) \\)" \
+				    "\\( d = \\( string = 'abcde', c = \\(\\.\\.\\.\\) \\) \\)" \
+				    "\\( d = \\( string = 'abcde', c = \\( b = \\(\\.\\.\\.\\), array = \\(\\.\\.\\.\\) \\) \\) \\)" \
+				    "\\( d = \\( string = 'abcde', c = \\( b = \\( a = 1 \\), array = \\(0, 0, 0, 0, 0\\) \\) \\) \\)" }
diff --git a/gdb/testsuite/gdb.fortran/max-depth.f90 b/gdb/testsuite/gdb.fortran/max-depth.f90
new file mode 100644
index 00000000000..9b6f90c2057
--- /dev/null
+++ b/gdb/testsuite/gdb.fortran/max-depth.f90
@@ -0,0 +1,42 @@
+! 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 2 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/>.
+
+program max_depth_test
+  type :: s1
+     integer :: a
+  end type s1
+
+  type :: s2
+     type (s1) :: b
+     integer :: array (5)
+  end type s2
+
+  type :: s3
+     character(5) :: string
+     type (s2) :: c
+  end type s3
+
+  type :: s4
+     type (s3) :: d
+  end type s4
+
+  logical :: l
+  type (s4) :: var
+
+  var%d%c%b%a = 1
+  var%d%c%array = 0
+  var%d%string = "abcde"
+  l = .FALSE.					! stop-here
+end program max_depth_test
diff --git a/gdb/testsuite/gdb.go/max-depth.exp b/gdb/testsuite/gdb.go/max-depth.exp
new file mode 100644
index 00000000000..0f8ba9ab83f
--- /dev/null
+++ b/gdb/testsuite/gdb.go/max-depth.exp
@@ -0,0 +1,40 @@
+# 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/>.
+
+# Test the use of 'set prinnt max-depth' when printing go structures.
+
+load_lib "go.exp"
+
+if { [skip_go_tests] } { continue }
+
+standard_testfile .go
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {debug go}] } {
+    return -1
+}
+
+if { [go_runto_main] < 0 } {
+    untested "could not run to main"
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "run to breakpoint" ".*Break here.*"
+
+gdb_print_expr_at_depths "s1" [list "{...}" \
+				    "{m_struct = {...}, m_string = $hex \"hello world\"}" \
+				    "{m_struct = {m_a = 3, m_b = 7}, m_string = $hex \"hello world\"}"]
diff --git a/gdb/testsuite/gdb.go/max-depth.go b/gdb/testsuite/gdb.go/max-depth.go
new file mode 100644
index 00000000000..6dbfc22b84d
--- /dev/null
+++ b/gdb/testsuite/gdb.go/max-depth.go
@@ -0,0 +1,41 @@
+// 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/>.
+
+package main
+
+import "fmt"
+
+type S1 struct {
+     m_a int
+     m_b int
+}
+
+type S2 struct {
+     m_struct S1
+     m_string string
+}
+
+func main () {
+  var s1 S2
+  var i int
+
+  s1.m_string = "hello world"
+  s1.m_struct.m_a = 3
+  s1.m_struct.m_b = 7
+
+  i = 0           // Break here.
+  fmt.Println ("Got: %d, %d, %d, %s", i, s1.m_struct.m_a,
+               s1.m_struct.m_b, s1.m_string)
+}
diff --git a/gdb/testsuite/gdb.modula2/max-depth.c b/gdb/testsuite/gdb.modula2/max-depth.c
new file mode 100644
index 00000000000..c2fd70ca632
--- /dev/null
+++ b/gdb/testsuite/gdb.modula2/max-depth.c
@@ -0,0 +1,45 @@
+/* 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/>.  */
+
+#include <string.h>
+
+struct S1
+{
+  int a;
+  int b;
+};
+
+struct S2
+{
+  char str[10];
+  struct S1 s1;
+};
+
+struct S3
+{
+  struct S2 s2;
+};
+
+int
+main ()
+{
+  struct S3 s3;
+
+  memcpy (s3.s2.str, "abcde\0fghi", 10);
+  s3.s2.s1.a = 3;
+  s3.s2.s1.b = 4;
+
+  return 0;	/* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.modula2/max-depth.exp b/gdb/testsuite/gdb.modula2/max-depth.exp
new file mode 100644
index 00000000000..ac7edfe8f72
--- /dev/null
+++ b/gdb/testsuite/gdb.modula2/max-depth.exp
@@ -0,0 +1,37 @@
+# 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/>.
+
+# Test the use of 'set prinnt max-depth' when printing modula2 structures.
+
+standard_testfile
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug quiet}]} {
+    return -1
+}
+
+if { ![runto_main] } then {
+    fail "can't run to main"
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "breakpoint"
+
+gdb_test "set lang modula-2" ".*does not match.*" "switch to modula-2"
+
+gdb_print_expr_at_depths "s3" [list "{...}" \
+				    "{s2 = {...}}" \
+				    "{s2 = {str = \"abcde\\\\000fghi\", s1 = {...}}}" \
+				    "{s2 = {str = \"abcde\\\\000fghi\", s1 = {a = 3, b = 4}}}"]
diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
index 2f574fbfa5a..ebb2074ce87 100644
--- a/gdb/testsuite/gdb.python/py-format-string.exp
+++ b/gdb/testsuite/gdb.python/py-format-string.exp
@@ -672,6 +672,26 @@ proc test_max_elements {} {
   }
 }
 
+# Test the max_depth option for gdb.Value.format_string.
+proc test_max_depth {} {
+    set opts "max_depth=-1"
+    with_test_prefix $opts {
+	check_format_string "a_struct_with_union" $opts
+    }
+    set opts "max_depth=0"
+    with_test_prefix $opts {
+	check_format_string "a_struct_with_union" $opts "\\{\.\.\.\\}"
+    }
+    set opts "max_depth=1"
+    with_test_prefix $opts {
+	check_format_string "a_struct_with_union" $opts "\\{the_union = \\{\.\.\.\\}\\}"
+    }
+    set opts "max_depth=2"
+    with_test_prefix $opts {
+	check_format_string "a_struct_with_union" $opts
+    }
+}
+
 # Test the repeat_threshold option for gdb.Value.format_string.
 proc test_repeat_threshold {} {
   global current_lang
@@ -925,6 +945,7 @@ proc test_all_common {} {
   test_actual_objects
   test_static_members
   test_max_elements
+  test_max_depth
   test_repeat_threshold
   test_format
   # Multiple options mixed together.
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.c b/gdb/testsuite/gdb.python/py-nested-maps.c
new file mode 100644
index 00000000000..94107c0180d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.c
@@ -0,0 +1,130 @@
+/* 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/>.  */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define FIXED_MAP_SIZE 10
+
+struct key_t
+{
+  int a;
+  int b;
+};
+
+struct value_t
+{
+  int x;
+  int y;
+  int z;
+};
+
+struct map_t
+{
+  const char *name;
+  int length;
+  struct key_t *keys;
+  struct value_t *values;
+
+  /* This field is used only by the pretty printer.  */
+  int show_header;
+};
+
+struct map_map_t
+{
+  int length;
+  struct map_t **values;
+
+  /* This field is used only by the pretty printer.  */
+  int show_header;
+};
+
+struct map_t *
+create_map (const char *name)
+{
+  struct map_t *m = malloc (sizeof (struct map_t));
+  m->name = strdup (name);
+  m->length = 0;
+  m->keys = NULL;
+  m->values = NULL;
+  m->show_header = 0;
+}
+
+void
+add_map_element (struct map_t *m, struct key_t k, struct value_t v)
+{
+  if (m->length == 0)
+    {
+      m->keys = malloc (sizeof (struct key_t) * FIXED_MAP_SIZE);
+      m->values = malloc (sizeof (struct value_t) * FIXED_MAP_SIZE);
+    }
+
+  m->keys[m->length] = k;
+  m->values[m->length] = v;
+  m->length++;
+}
+
+struct map_map_t *
+create_map_map (void)
+{
+  struct map_map_t *mm = malloc (sizeof (struct map_map_t));
+  mm->length = 0;
+  mm->values = NULL;
+  mm->show_header = 0;
+}
+
+void
+add_map_map_element (struct map_map_t *mm, struct map_t *map)
+{
+  if (mm->length == 0)
+    mm->values = malloc (sizeof (struct map_t *) * FIXED_MAP_SIZE);
+
+  mm->values[mm->length] = map;
+  mm->length++;
+}
+
+int
+main (void)
+{
+  struct map_t *m1 = create_map ("m1");
+  struct key_t k1 = {3, 4};
+  struct key_t k2 = {4, 5};
+  struct key_t k3 = {5, 6};
+  struct key_t k4 = {6, 7};
+  struct key_t k5 = {7, 8};
+  struct key_t k6 = {8, 9};
+  struct value_t v1 = {0, 1, 2};
+  struct value_t v2 = {3, 4, 5};
+  struct value_t v3 = {6, 7, 8};
+  struct value_t v4 = {9, 0, 1};
+  struct value_t v5 = {2, 3, 4};
+  struct value_t v6 = {5, 6, 7};
+  add_map_element (m1, k1, v1);
+  add_map_element (m1, k2, v2);
+  add_map_element (m1, k3, v3);
+
+  struct map_t *m2 = create_map ("m2");
+  add_map_element (m2, k4, v4);
+  add_map_element (m2, k5, v5);
+  add_map_element (m2, k6, v6);
+
+  struct map_map_t *mm = create_map_map ();
+  add_map_map_element (mm, m1);
+  add_map_map_element (mm, m2);
+
+  return 0; /* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.exp b/gdb/testsuite/gdb.python/py-nested-maps.exp
new file mode 100644
index 00000000000..81dd99ca2c9
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.exp
@@ -0,0 +1,238 @@
+# Copyright (C) 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 tests GDB's python pretty printing of nested map like
+# structures using structures as keys and values, it then tests how
+# 'set print max-depth' affects this printing.
+
+load_lib gdb-python.exp
+
+standard_testfile
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug}] } {
+    return -1
+}
+
+if ![runto_main ] then {
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "Break here"]
+gdb_continue_to_breakpoint "run to testing point" ".*Break here.*"
+
+set remote_python_file [gdb_remote_download host \
+			    ${srcdir}/${subdir}/${testfile}.py]
+gdb_test_no_output "source ${remote_python_file}" "load python file"
+
+# Test printing with 'set print pretty off'.
+gdb_test_no_output "set print pretty off"
+with_test_prefix "pretty=off" {
+    gdb_print_expr_at_depths "*m1" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     "\{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
+	     "\{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
+	    ]
+
+    gdb_print_expr_at_depths "*mm" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
+	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
+	    ]
+}
+
+# Now again, but with 'set print pretty on'.
+gdb_test_no_output "set print pretty on"
+with_test_prefix "pretty=on" {
+    gdb_print_expr_at_depths "*m1" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[\{" \
+		  "    a = 3," \
+		  "    b = 4" \
+		  "  \}\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[\{" \
+		  "    a = 4," \
+		  "    b = 5" \
+		  "  \}\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[\{" \
+		  "    a = 5," \
+		  "    b = 6" \
+		  "  \}\\\] = \{\\.\\.\\.\}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[\{" \
+		  "    a = 3," \
+		  "    b = 4" \
+		  "  \}\\\] = \{" \
+		  "    x = 0," \
+		  "    y = 1," \
+		  "    z = 2" \
+		  "  \}," \
+		  "  \\\[\{" \
+		  "    a = 4," \
+		  "    b = 5" \
+		  "  \}\\\] = \{" \
+		  "    x = 3," \
+		  "    y = 4," \
+		  "    z = 5" \
+		  "  \}," \
+		  "  \\\[\{" \
+		  "    a = 5," \
+		  "    b = 6" \
+		  "  \}\\\] = \{" \
+		  "    x = 6," \
+		  "    y = 7," \
+		  "    z = 8" \
+		  "  \}" \
+		  "\}" ] \
+	    ]
+
+     gdb_print_expr_at_depths "*mm" \
+	[list \
+	     "\{\\.\\.\\.\}" \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}," \
+		  "  \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 3," \
+		  "      b = 4" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 4," \
+		  "      b = 5" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 5," \
+		  "      b = 6" \
+		  "    \}\\\] = \{\\.\\.\\.\}" \
+		  "  \}," \
+		  "  \\\[$hex \"m2\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 6," \
+		  "      b = 7" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 7," \
+		  "      b = 8" \
+		  "    \}\\\] = \{\\.\\.\\.\}," \
+		  "    \\\[\{" \
+		  "      a = 8," \
+		  "      b = 9" \
+		  "    \}\\\] = \{\\.\\.\\.\}" \
+		  "  \}" \
+		  "\}" ] \
+	     [multi_line \
+		  " = \{" \
+		  "  \\\[$hex \"m1\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 3," \
+		  "      b = 4" \
+		  "    \}\\\] = \{" \
+		  "      x = 0," \
+		  "      y = 1," \
+		  "      z = 2" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 4," \
+		  "      b = 5" \
+		  "    \}\\\] = \{" \
+		  "      x = 3," \
+		  "      y = 4," \
+		  "      z = 5" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 5," \
+		  "      b = 6" \
+		  "    \}\\\] = \{" \
+		  "      x = 6," \
+		  "      y = 7," \
+		  "      z = 8" \
+		  "    \}" \
+		  "  \}," \
+		  "  \\\[$hex \"m2\"\\\] = \{" \
+		  "    \\\[\{" \
+		  "      a = 6," \
+		  "      b = 7" \
+		  "    \}\\\] = \{" \
+		  "      x = 9," \
+		  "      y = 0," \
+		  "      z = 1" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 7," \
+		  "      b = 8" \
+		  "    \}\\\] = \{" \
+		  "      x = 2," \
+		  "      y = 3," \
+		  "      z = 4" \
+		  "    \}," \
+		  "    \\\[\{" \
+		  "      a = 8," \
+		  "      b = 9" \
+		  "    \}\\\] = \{" \
+		  "      x = 5," \
+		  "      y = 6," \
+		  "      z = 7" \
+		  "    \}" \
+		  "  \}" \
+		  "\}" ] \
+	    ]
+}
+
+# Test printing with 'set print pretty off', but this time display a
+# top level string (returned from the to_string method) as part of the
+# printed value.
+gdb_test_no_output "set mm->show_header=1"
+gdb_test_no_output "set m1->show_header=1"
+gdb_test_no_output "set m2->show_header=1"
+with_test_prefix "headers=on" {
+    gdb_test_no_output "set print pretty off"
+    with_test_prefix "pretty=off" {
+	gdb_print_expr_at_depths "*m1" \
+	    [list \
+		 "\{\\.\\.\\.\}" \
+		 "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
+		 "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
+		]
+
+	gdb_print_expr_at_depths "*mm" \
+	    [list \
+		 "\{\\.\\.\\.\}" \
+		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
+		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
+		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
+		]
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-nested-maps.py b/gdb/testsuite/gdb.python/py-nested-maps.py
new file mode 100644
index 00000000000..d3fdf59533d
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-nested-maps.py
@@ -0,0 +1,135 @@
+# Copyright (C) 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 GDB's printing of
+# nested map like structures.
+
+import re
+import gdb
+
+def _iterator1 (pointer, len):
+    while len > 0:
+        map = pointer.dereference()
+        yield ('', map['name'])
+        yield ('', map.dereference())
+        pointer += 1
+        len -= 1
+
+def _iterator2 (pointer1, pointer2, len):
+    while len > 0:
+        yield ("", pointer1.dereference())
+        yield ("", pointer2.dereference())
+        pointer1 += 1
+        pointer2 += 1
+        len -= 1
+
+class pp_map (object):
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        if (self.val['show_header'] == 0):
+            return None
+        else:
+            return "pp_map"
+
+    def children(self):
+        return _iterator2(self.val['keys'],
+                          self.val['values'],
+                          self.val['length'])
+
+    def display_hint (self):
+        return 'map'
+
+class pp_map_map (object):
+    def __init__(self, val):
+        self.val = val
+
+    def to_string(self):
+        if (self.val['show_header'] == 0):
+            return None
+        else:
+            return "pp_map_map"
+
+    def children(self):
+        return _iterator1(self.val['values'],
+                          self.val['length'])
+
+    def display_hint (self):
+        return 'map'
+
+def 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 == None:
+        return None
+
+    # Iterate over local dictionary of types to determine
+    # if a printer is registered for that type.  Return an
+    # instantiation of the printer if found.
+    for function in pretty_printers_dict:
+        if function.match (typename):
+            return pretty_printers_dict[function] (val)
+
+    # Cannot find a pretty printer.  Return None.
+    return None
+
+# Lookup a printer for VAL in the typedefs dict.
+def lookup_typedefs_function (val):
+    "Look-up and return a pretty-printer that can print val (typedefs)."
+
+    # Get the type.
+    type = val.type
+
+    if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF:
+        return None
+
+    # Iterate over local dictionary of typedef types to determine if a
+    # printer is registered for that type.  Return an instantiation of
+    # the printer if found.
+    for function in typedefs_pretty_printers_dict:
+        if function.match (type.name):
+            return typedefs_pretty_printers_dict[function] (val)
+
+    # Cannot find a pretty printer.
+    return None
+
+def register_pretty_printers ():
+    pretty_printers_dict[re.compile ('^struct map_t$')] = pp_map
+    pretty_printers_dict[re.compile ('^map_t$')] = pp_map
+    pretty_printers_dict[re.compile ('^struct map_map_t$')] = pp_map_map
+    pretty_printers_dict[re.compile ('^map_map_t$')] = pp_map_map
+
+# Dict for struct types with typedefs fully stripped.
+pretty_printers_dict = {}
+# Dict for typedef types.
+typedefs_pretty_printers_dict = {}
+
+register_pretty_printers ()
+gdb.pretty_printers.append (lookup_function)
+gdb.pretty_printers.append (lookup_typedefs_function)
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 6c0f243eef8..70e5dd0d540 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -1390,6 +1390,36 @@ proc gdb_test_stdio {command inferior_pattern {gdb_pattern ""} {message ""}} {
     return $res
 }
 
+# get_print_expr_at_depths EXP OUTPUTS
+#
+# Used for testing 'set print max-depth'.  Prints the expression EXP
+# with 'set print max-depth' set to various depths.  OUTPUTS is a list
+# of `n` different patterns to match at each of the depths from 0 to
+# (`n` - 1).
+#
+# This proc does one final check with the max-depth set to 'unlimited'
+# which is tested against the last pattern in the OUTPUTS list.  The
+# OUTPUTS list is therefore required to match every depth from 0 to a
+# depth where the whole of EXP is printed with no ellipsis.
+#
+# This proc leaves the 'set print max-depth' set to 'unlimited'.
+proc gdb_print_expr_at_depths {exp outputs} {
+    for { set depth 0 } { $depth <= [llength $outputs] } { incr depth } {
+	if { $depth == [llength $outputs] } {
+	    set expected_result [lindex $outputs [expr [llength $outputs] - 1]]
+	    set depth_string "unlimited"
+	} else {
+	    set expected_result [lindex $outputs $depth]
+	    set depth_string $depth
+	}
+
+	with_test_prefix "exp='$exp': depth=${depth_string}" {
+	    gdb_test_no_output "set print max-depth ${depth_string}"
+	    gdb_test "p $exp" "$expected_result"
+	}
+    }
+}
+
 \f
 
 # Issue a PASS and return true if evaluating CONDITION in the caller's
diff --git a/gdb/valprint.c b/gdb/valprint.c
index b02ebf6c272..0447c715249 100644
--- a/gdb/valprint.c
+++ b/gdb/valprint.c
@@ -88,6 +88,7 @@ static void val_print_type_code_flags (struct type *type,
 				       struct ui_file *stream);
 
 #define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
+#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
 
 struct value_print_options user_print_options =
 {
@@ -109,7 +110,8 @@ struct value_print_options user_print_options =
   1,				/* pascal_static_field_print */
   0,				/* raw */
   0,				/* summary */
-  1				/* symbol_print */
+  1,				/* symbol_print */
+  PRINT_MAX_DEPTH_DEFAULT	/* max_depth */
 };
 
 /* Initialize *OPTS to be a copy of the user print options.  */
@@ -281,6 +283,18 @@ val_print_scalar_type_p (struct type *type)
     }
 }
 
+/* A helper function for val_print.  When printing with limited depth we
+   want to print string and scalar arguments, but not aggregate arguments.
+   This function distinguishes between the two.  */
+
+static bool
+val_print_scalar_or_string_type_p (struct type *type,
+				   const struct language_defn *language)
+{
+  return (val_print_scalar_type_p (type)
+	  || language->la_is_string_type_p (type));
+}
+
 /* See its definition in value.h.  */
 
 int
@@ -1054,6 +1068,11 @@ val_print (struct type *type, LONGEST embedded_offset,
       return;
     }
 
+  /* If this value is too deep then don't print it.  */
+  if (!val_print_scalar_or_string_type_p (type, language)
+      && val_print_check_max_depth (stream, recurse, options, language))
+    return;
+
   try
     {
       language->la_val_print (type, embedded_offset, address,
@@ -1066,6 +1085,22 @@ val_print (struct type *type, LONGEST embedded_offset,
     }
 }
 
+/* See valprint.h.  */
+
+bool val_print_check_max_depth (struct ui_file *stream, int recurse,
+				const struct value_print_options *options,
+				const struct language_defn *language)
+{
+  if (options->max_depth > -1 && recurse >= options->max_depth)
+    {
+      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);
+      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
+      return true;
+    }
+
+  return false;
+}
+
 /* Check whether the value VAL is printable.  Return 1 if it is;
    return 0 and print an appropriate error message to STREAM according to
    OPTIONS if it is not.  */
@@ -2859,6 +2894,15 @@ val_print_string (struct type *elttype, const char *encoding,
 
   return (bytes_read / width);
 }
+
+/* Handle 'show print max-depth'.  */
+
+static void
+show_print_max_depth (struct ui_file *file, int from_tty,
+		      struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Maximum print depth is %s.\n"), value);
+}
 \f
 
 /* The 'set input-radix' command writes to this auxiliary variable.
@@ -3152,4 +3196,14 @@ Use 'show input-radix' or 'show output-radix' to independently show each."),
 Set printing of array indexes."), _("\
 Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
                            &setprintlist, &showprintlist);
+
+  add_setshow_zuinteger_unlimited_cmd ("max-depth", class_support,
+                            &user_print_options.max_depth, _("\
+Set maximum print depth for nested structures, unions and arrays."), _("\
+Show maximum print depth for nested structures, unions, and arrays."), _("\
+When structures, unions, or arrays are nested beyond this depth then they\n\
+will be replaced with either '{...}' or '(...)' depending on the language.\n\
+Use 'set print max-depth unlimited' to print the complete structure."),
+				       NULL, show_print_max_depth,
+				       &setprintlist, &showprintlist);
 }
diff --git a/gdb/valprint.h b/gdb/valprint.h
index db99b52b2ad..e5cc9477987 100644
--- a/gdb/valprint.h
+++ b/gdb/valprint.h
@@ -92,6 +92,9 @@ struct value_print_options
   /* If nonzero, when printing a pointer, print the symbol to which it
      points, if any.  */
   int symbol_print;
+
+  /* Maximum print depth when printing nested aggregates.  */
+  int max_depth;
 };
 
 /* The global print options set by the user.  In general this should
@@ -246,4 +249,13 @@ extern int build_address_symbolic (struct gdbarch *,
 				   int *line,
 				   int *unmapped);
 
+/* Check to see if RECURSE is greater than or equal to the allowed
+   printing max-depth (see 'set print max-depth').  If it is then print an
+   ellipsis expression to STREAM and return true, otherwise return false.
+   LANGUAGE determines what type of ellipsis expression is printed.  */
+
+extern bool val_print_check_max_depth (struct ui_file *stream, int recurse,
+				       const struct value_print_options *opts,
+				       const struct language_defn *language);
+
 #endif
-- 
2.14.5

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
@ 2019-04-17  7:42     ` Philippe Waroquiers
  2019-04-17 14:35     ` Eli Zaretskii
  2019-04-18 17:08     ` Pedro Alves
  2 siblings, 0 replies; 33+ messages in thread
From: Philippe Waroquiers @ 2019-04-17  7:42 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Nice feature, thanks for this patch.
Just one small comment about the manual:
I think the table presentation on how depth settings
affect printing as given below in the log message
is easier to read than the paragraphs showing the same
examples in the manual.
But of course, feel free to ignore this comment, this
is just personal preference.


Note that when this max-depth feature is in, I will update
the / command patch to support this setting, as changing
temporarily the max depth will be very handy.

Philippe

On Wed, 2019-04-17 at 00:06 +0100, Andrew Burgess wrote:
> Introduce a new print setting max-depth which can be set with 'set
> print max-depth DEPTH'.  The default value of DEPTH is 20, but this
> can also be set to unlimited.
> 
> When GDB is printing a value containing nested structures GDB will
> stop descending at depth DEPTH.  Here is a small example:
> 
>     typedef struct s1 { int a; } s1;
>     typedef struct s2 { s1 b; } s2;
>     typedef struct s3 { s2 c; } s3;
>     typedef struct s4 { s3 d; } s4;
> 
>     s4 var = { { { { 3 } } } };
> 
> The following table shows how various depth settings affect printing
> of 'var':
> 
>     | Depth Setting | Result of 'p var'              |
>     |---------------+--------------------------------|
>     |     Unlimited | $1 = {d = {c = {b = {a = 3}}}} |
>     |             4 | $1 = {d = {c = {b = {a = 3}}}} |
>     |             3 | $1 = {d = {c = {b = {...}}}}   |
>     |             2 | $1 = {d = {c = {...}}}         |
>     |             1 | $1 = {d = {...}}               |
>     |             0 | $1 = {...}                     |
> 
> Only structures, unions, and arrays are replaced in this way, scalars
> and strings are not replaced.
> 
> The replacement is counted from the level at which you print, not from
> the top level of the structure.  So, consider the above example and
> this GDB session:
> 
>     (gdb) set print max-depth 2
>     (gdb) p var
>     $1 = {d = {c = {...}}}
>     (gdb) p var.d
>     $2 = {c = {b = {...}}}
>     (gdb) p var.d.c
>     $3 = {b = {a = 3}}
> 
> Setting the max-depth to 2 doesn't prevent the user from exploring
> deeper into 'var' by asking for specific sub-fields to be printed.
> 
> The motivation behind this feature is to try and give the user more
> control over how much is printed when examining large, complex data
> structures.
> 
> The default max-depth of 20 means that there is a change in GDB's
> default behaviour.  Someone printing a data structure with 20 levels
> of nesting will now see '{...}' instead of their data, they would need
> to adjust the max depth, or call print again naming a specific field
> in order to dig deeper into their data structure.  If this is
> considered a problem then we could increase the default, or even make
> the default unlimited.
> 
> This commit relies on the previous commit, which added a new field to
> the language structure, this new field was a string that contained the
> pattern that should be used when a structure/union/array is replaced
> in the output, this allows languages to use a syntax that is more
> appropriate, mostly this will be selecting the correct types of
> bracket '(...)' or '{...}', both of which are currently in use.
> 
> This commit should have no impact on MI output, expressions are
> printed through the MI using -var-create and then -var-list-children.
> As each use of -var-list-children only ever displays a single level of
> an expression then the max-depth setting will have no impact.
> 
> This commit also adds the max-depth mechanism to the scripting
> language pretty printers following basically the same rules as for the
> built in value printing.
> 
> One quirk is that when printing a value using the display hint 'map',
> if the keys of the map are structs then GDB will hide the keys one
> depth level after it hides the values, this ensures that GDB produces
> output like this:
> 
>   $1 = map_object = {[{key1}] = {...}, [{key2}] = {...}}
> 
> Instead of this less helpful output:
> 
>   $1 = map_object = {[{...}] = {...}, [{...}] = {...}}
> 
> This is covered by the new tests in gdb.python/py-nested-maps.exp.
> 
> gdb/ChangeLog:
> 
> 	* cp-valprint.c (cp_print_value_fields): Allow an additional level
> 	of depth when printing anonymous structs or unions.
> 	* guile/scm-pretty-print.c (gdbscm_apply_val_pretty_printer):
> 	Don't print either the top-level value, or the children if the
> 	max-depth is exceeded.
> 	(ppscm_print_children): When printing the key of a map, allow one
> 	extra level of depth.
> 	* python/py-prettyprint.c (gdbpy_apply_val_pretty_printer): Don't
> 	print either the top-level value, or the children if the max-depth
> 	is exceeded.
> 	(print_children): When printing the key of a map, allow one extra
> 	level of depth.
> 	* python/py-value.c (valpy_format_string): Add max_depth keyword.
> 	* valprint.c: (PRINT_MAX_DEPTH_DEFAULT): Define.
> 	(user_print_options): Initialise max_depth field.
> 	(val_print_scalar_or_string_type_p): New function.
> 	(val_print): Check to see if the max depth has been reached.
> 	(val_print_check_max_depth): Define new function.
> 	(show_print_max_depth): New function.
> 	(_initialize_valprint): Add 'print max-depth' option.
> 	* valprint.h (struct value_print_options) <max_depth>: New field.
> 	(val_print_check_max_depth): Declare new function.
> 	* NEWS: Document new feature.
> 
> gdb/doc/ChangeLog:
> 
> 	* gdb.texinfo (Print Settings): Document 'print max-depth'.
> 	* guile.texi (Guile Pretty Printing API): Document that 'print
> 	max-depth' can effect the display of a values children.
> 	* python.texi (Pretty Printing API): Likewise.
> 	(Values From Inferior): Document max_depth keyword.
> 
> gdb/testsuite/ChangeLog:
> 
> 	* gdb.base/max-depth.c: New file.
> 	* gdb.base/max-depth.exp: New file.
> 	* gdb.python/py-nested-maps.c: New file.
> 	* gdb.python/py-nested-maps.exp: New file.
> 	* gdb.python/py-nested-maps.py: New file.
> 	* gdb.python/py-format-string.exp (test_max_depth): New proc.
> 	(test_all_common): Call test_max_depth.
> 	* gdb.fortran/max-depth.exp: New file.
> 	* gdb.fortran/max-depth.f90: New file.
> 	* gdb.go/max-depth.exp: New file.
> 	* gdb.go/max-depth.go: New file.
> 	* gdb.modula2/max-depth.exp: New file.
> 	* gdb.modula2/max-depth.c: New file.
> 	* lib/gdb.exp (get_print_expr_at_depths): New proc.
> ---
>  gdb/ChangeLog                                 |  30 ++++
>  gdb/NEWS                                      |   9 +
>  gdb/cp-valprint.c                             |  80 +++++----
>  gdb/doc/ChangeLog                             |   8 +
>  gdb/doc/gdb.texinfo                           |  70 ++++++++
>  gdb/doc/guile.texi                            |   3 +
>  gdb/doc/python.texi                           |   8 +
>  gdb/guile/scm-pretty-print.c                  |  19 +-
>  gdb/python/py-prettyprint.c                   |  15 +-
>  gdb/python/py-value.c                         |   7 +-
>  gdb/testsuite/ChangeLog                       |  18 ++
>  gdb/testsuite/gdb.base/max-depth.c            | 232 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.base/max-depth.exp          | 165 ++++++++++++++++++
>  gdb/testsuite/gdb.fortran/max-depth.exp       |  41 +++++
>  gdb/testsuite/gdb.fortran/max-depth.f90       |  42 +++++
>  gdb/testsuite/gdb.go/max-depth.exp            |  40 +++++
>  gdb/testsuite/gdb.go/max-depth.go             |  41 +++++
>  gdb/testsuite/gdb.modula2/max-depth.c         |  45 +++++
>  gdb/testsuite/gdb.modula2/max-depth.exp       |  37 ++++
>  gdb/testsuite/gdb.python/py-format-string.exp |  21 +++
>  gdb/testsuite/gdb.python/py-nested-maps.c     | 130 ++++++++++++++
>  gdb/testsuite/gdb.python/py-nested-maps.exp   | 238 ++++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-nested-maps.py    | 135 +++++++++++++++
>  gdb/testsuite/lib/gdb.exp                     |  30 ++++
>  gdb/valprint.c                                |  56 +++++-
>  gdb/valprint.h                                |  12 ++
>  26 files changed, 1496 insertions(+), 36 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/max-depth.c
>  create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
>  create mode 100644 gdb/testsuite/gdb.go/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.go/max-depth.go
>  create mode 100644 gdb/testsuite/gdb.modula2/max-depth.c
>  create mode 100644 gdb/testsuite/gdb.modula2/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 5309a8f923b..26bebf7b48c 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -19,6 +19,15 @@
>  * Two new convernience functions $_cimag and $_creal that extract the
>    imaginary and real parts respectively from complex numbers.
>  
> +* New commands
> +
> +set print max-depth
> +show print max-depth
> +  Allows deeply nested structures to be simplified when printing by
> +  replacing deeply nested parts (beyond the max-depth) with ellipses.
> +  The default max-depth is 20, but this can be set to unlimited to get
> +  the old behavior back.
> +
>  * Python API
>  
>    ** The gdb.Value type has a new method 'format_string' which returns a
> diff --git a/gdb/cp-valprint.c b/gdb/cp-valprint.c
> index e883179dfad..ff860df499a 100644
> --- a/gdb/cp-valprint.c
> +++ b/gdb/cp-valprint.c
> @@ -272,10 +272,23 @@ cp_print_value_fields (struct type *type, struct type *real_type,
>  				   current_language->la_language,
>  				   DMGL_PARAMS | DMGL_ANSI);
>  	  annotate_field_name_end ();
> +
> +	  /* We tweak various options in a few cases below.  */
> +	  value_print_options options_copy = *options;
> +	  value_print_options *opts = &options_copy;
> +
>  	  /* Do not print leading '=' in case of anonymous
>  	     unions.  */
>  	  if (strcmp (TYPE_FIELD_NAME (type, i), ""))
>  	    fputs_filtered (" = ", stream);
> +	  else
> +	    {
> +	      /* If this is an anonymous field then we want to consider it
> +		 as though it is at its parent's depth when it comes to the
> +		 max print depth.  */
> +	      if (opts->max_depth != -1 && opts->max_depth < INT_MAX)
> +		++opts->max_depth;
> +	    }
>  	  annotate_field_value ();
>  
>  	  if (!field_is_static (&TYPE_FIELD (type, i))
> @@ -299,14 +312,12 @@ cp_print_value_fields (struct type *type, struct type *real_type,
>  		}
>  	      else
>  		{
> -		  struct value_print_options opts = *options;
> -
> -		  opts.deref_ref = 0;
> +		  opts->deref_ref = 0;
>  
>  		  v = value_field_bitfield (type, i, valaddr, offset, val);
>  
> -		  common_val_print (v, stream, recurse + 1, &opts,
> -				    current_language);
> +		  common_val_print (v, stream, recurse + 1,
> +				    opts, current_language);
>  		}
>  	    }
>  	  else
> @@ -333,8 +344,7 @@ cp_print_value_fields (struct type *type, struct type *real_type,
>  		    }
>  
>  		  cp_print_static_field (TYPE_FIELD_TYPE (type, i),
> -					 v, stream, recurse + 1,
> -					 options);
> +					 v, stream, recurse + 1, opts);
>  		}
>  	      else if (i == vptr_fieldno && type == vptr_basetype)
>  		{
> @@ -346,20 +356,18 @@ cp_print_value_fields (struct type *type, struct type *real_type,
>  		      CORE_ADDR addr;
>  		      
>  		      addr = extract_typed_address (valaddr + i_offset, i_type);
> -		      print_function_pointer_address (options,
> +		      print_function_pointer_address (opts,
>  						      get_type_arch (type),
>  						      addr, stream);
>  		    }
>  		}
>  	      else
>  		{
> -		  struct value_print_options opts = *options;
> -
> -		  opts.deref_ref = 0;
> +		  opts->deref_ref = 0;
>  		  val_print (TYPE_FIELD_TYPE (type, i),
>  			     offset + TYPE_FIELD_BITPOS (type, i) / 8,
>  			     address,
> -			     stream, recurse + 1, val, &opts,
> +			     stream, recurse + 1, val, opts,
>  			     current_language);
>  		}
>  	    }
> @@ -575,25 +583,35 @@ cp_print_value (struct type *type, struct type *real_type,
>  	{
>  	  int result = 0;
>  
> -	  /* Attempt to run an extension language pretty-printer on the
> -	     baseclass if possible.  */
> -	  if (!options->raw)
> -	    result
> -	      = apply_ext_lang_val_pretty_printer (baseclass,
> -						   thisoffset + boffset,
> -						   value_address (base_val),
> -						   stream, recurse,
> -						   base_val, options,
> -						   current_language);
> -
> -	  if (!result)
> -	    cp_print_value_fields (baseclass, thistype,
> -				   thisoffset + boffset,
> -				   value_address (base_val),
> -				   stream, recurse, base_val, options,
> -				   ((struct type **)
> -				    obstack_base (&dont_print_vb_obstack)),
> -				   0);
> +	  if (options->max_depth > -1
> +	      && recurse >= options->max_depth)
> +	    {
> +	      const struct language_defn *language = current_language;
> +	      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);
> +	      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
> +	    }
> +	  else
> +	    {
> +	      /* Attempt to run an extension language pretty-printer on the
> +		 baseclass if possible.  */
> +	      if (!options->raw)
> +		result
> +		  = apply_ext_lang_val_pretty_printer (baseclass,
> +						       thisoffset + boffset,
> +						       value_address (base_val),
> +						       stream, recurse,
> +						       base_val, options,
> +						       current_language);
> +
> +	      if (!result)
> +		cp_print_value_fields (baseclass, thistype,
> +				       thisoffset + boffset,
> +				       value_address (base_val),
> +				       stream, recurse, base_val, options,
> +				       ((struct type **)
> +					obstack_base (&dont_print_vb_obstack)),
> +				       0);
> +	    }
>  	}
>        fputs_filtered (", ", stream);
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index f410d026b82..8c0661d7ca8 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -10560,6 +10560,76 @@
>  Display the current threshold for printing repeated identical
>  elements.
>  
> +@item set print max-depth @var{depth}
> +@item set print max-depth unlimited
> +@cindex printing nested structures
> +Set the threshold after which nested structures are replaced with
> +ellipsis, this can make visualising deeply nested structures easier.
> +
> +For example, given this C code
> +
> +@smallexample
> +typedef struct s1 @{ int a; @} s1;
> +typedef struct s2 @{ s1 b; @} s2;
> +typedef struct s3 @{ s2 c; @} s3;
> +typedef struct s4 @{ s3 d; @} s4;
> +
> +s4 var = @{ @{ @{ @{ 3 @} @} @} @};
> +@end smallexample
> +
> +@noindent
> +And with @code{set print max-depth unlimited} in effect @samp{p var}
> +would print
> +
> +@smallexample
> +$1 = @{d = @{c = @{b = @{a = 3@}@}@}@}
> +@end smallexample
> +
> +@noindent
> +With @code{set print max-depth 0} in effect @samp{p var}
> +would print
> +
> +@smallexample
> +$1 = @{...@}
> +@end smallexample
> +
> +@noindent
> +With @code{set print max-depth 1} in effect @samp{p var}
> +would print
> +
> +@smallexample
> +$1 = @{d = @{...@}@}
> +@end smallexample
> +
> +@noindent
> +With @code{set print max-depth 2} in effect @samp{p var}
> +would print
> +
> +@smallexample
> +$1 = @{d = @{c = @{...@}@}@}
> +@end smallexample
> +
> +To see the contents of structures that have been hidden the user can
> +either increase the print max-depth to increase, or they can print
> +elements of structures that are visible, for example
> +
> +@smallexample
> +(gdb) p var
> +$1 = @{d = @{c = @{...@}@}@}
> +(gdb) p var.d
> +$2 = @{c = @{b = @{...@}@}@}
> +(gdb) p var.d.c
> +$3 = @{b = @{a = 3@}@}
> +@end smallexample
> +
> +The pattern used to replace nested structures varies based on
> +language, for most languages @code{@{...@}} is used, but Fortran uses
> +@code{(...)}.
> +
> +@item show print max-depth
> +Display the current threshold after which nested structures are
> +replaces with ellipsis.
> +
>  @item set print null-stop
>  @cindex @sc{null} elements in arrays
>  Cause @value{GDBN} to stop printing the characters of an array when the first
> diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi
> index 721338b050c..f4c29dc3df8 100644
> --- a/gdb/doc/guile.texi
> +++ b/gdb/doc/guile.texi
> @@ -1469,6 +1469,9 @@
>  
>  If @var{children} is @code{#f}, @value{GDBN} will act
>  as though the value has no children.
> +
> +Children may be hidden from display based on the value of @samp{set
> +print max-depth} (@pxref{Print Settings}).
>  @end table
>  @end deffn
>  
> diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
> index 36947de30b0..b47c38deef7 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -931,6 +931,11 @@
>  number of elements (see @code{set print elements} in @ref{Print
>  Settings}).
>  
> +@item max_depth
> +The maximum depth to print for nested structs and unions, or @code{-1}
> +to print an unlimited number of elements (see @code{set print
> +max-depth} 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
> @@ -1365,6 +1370,9 @@
>  
>  This method is optional.  If it does not exist, @value{GDBN} will act
>  as though the value has no children.
> +
> +Children may be hidden from display based on the value of @samp{set
> +print max-depth} (@pxref{Print Settings}).
>  @end defun
>  
>  @defun pretty_printer.display_hint (self)
> diff --git a/gdb/guile/scm-pretty-print.c b/gdb/guile/scm-pretty-print.c
> index 656c4bbc52e..630a062148e 100644
> --- a/gdb/guile/scm-pretty-print.c
> +++ b/gdb/guile/scm-pretty-print.c
> @@ -897,7 +897,18 @@ ppscm_print_children (SCM printer, enum display_hint hint,
>  	      ppscm_print_exception_unless_memory_error (except_scm, stream);
>  	      break;
>  	    }
> -	  common_val_print (value, stream, recurse + 1, options, language);
> +	  else
> +	    {
> +	      /* When printing the key of a map we allow one additional
> +		 level of depth.  This means the key will print before the
> +		 value does.  */
> +	      struct value_print_options opt = *options;
> +	      if (is_map && i % 2 == 0
> +		  && opt.max_depth != -1
> +		  && opt.max_depth < INT_MAX)
> +		++opt.max_depth;
> +	      common_val_print (value, stream, recurse + 1, &opt, language);
> +	    }
>  	}
>  
>        if (is_map && i % 2 == 0)
> @@ -984,6 +995,12 @@ gdbscm_apply_val_pretty_printer (const struct extension_language_defn *extlang,
>      }
>    gdb_assert (ppscm_is_pretty_printer_worker (printer));
>  
> +  if (val_print_check_max_depth (stream, recurse, options, language))
> +    {
> +      result = EXT_LANG_RC_OK;
> +      goto done;
> +    }
> +
>    /* If we are printing a map, we want some special formatting.  */
>    hint = ppscm_get_display_hint_enum (printer);
>    if (hint == HINT_ERROR)
> diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
> index 7760cab0ae0..fdc520d8443 100644
> --- a/gdb/python/py-prettyprint.c
> +++ b/gdb/python/py-prettyprint.c
> @@ -517,7 +517,17 @@ print_children (PyObject *printer, const char *hint,
>  	      error (_("Error while executing Python code."));
>  	    }
>  	  else
> -	    common_val_print (value, stream, recurse + 1, options, language);
> +	    {
> +	      /* When printing the key of a map we allow one additional
> +		 level of depth.  This means the key will print before the
> +		 value does.  */
> +	      struct value_print_options opt = *options;
> +	      if (is_map && i % 2 == 0
> +		  && opt.max_depth != -1
> +		  && opt.max_depth < INT_MAX)
> +		++opt.max_depth;
> +	      common_val_print (value, stream, recurse + 1, &opt, language);
> +	    }
>  	}
>  
>        if (is_map && i % 2 == 0)
> @@ -590,6 +600,9 @@ gdbpy_apply_val_pretty_printer (const struct extension_language_defn *extlang,
>    if (printer == Py_None)
>      return EXT_LANG_RC_NOP;
>  
> +  if (val_print_check_max_depth (stream, recurse, options, language))
> +    return EXT_LANG_RC_OK;
> +
>    /* If we are printing a map, we want some special formatting.  */
>    gdb::unique_xmalloc_ptr<char> hint (gdbpy_get_display_hint (printer.get ()));
>  
> diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
> index d3f4de40540..cbc30aa8c3e 100644
> --- a/gdb/python/py-value.c
> +++ b/gdb/python/py-value.c
> @@ -623,6 +623,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
>        "static_members",		/* See set print static-members on|off.  */
>        /* C non-bool options.  */
>        "max_elements", 		/* See set print elements N.  */
> +      "max_depth",		/* See set print max-depth N.  */
>        "repeat_threshold",	/* See set print repeats.  */
>        "format",			/* The format passed to the print command.  */
>        NULL
> @@ -665,7 +666,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
>    char *format = NULL;
>    if (!gdb_PyArg_ParseTupleAndKeywords (args,
>  					kw,
> -					"|O!O!O!O!O!O!O!O!O!IIs",
> +					"|O!O!O!O!O!O!O!O!O!IIIs",
>  					keywords,
>  					&PyBool_Type, &raw_obj,
>  					&PyBool_Type, &pretty_arrays_obj,
> @@ -677,6 +678,7 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
>  					&PyBool_Type, &actual_objects_obj,
>  					&PyBool_Type, &static_members_obj,
>  					&opts.print_max,
> +					&opts.max_depth,
>  					&opts.repeat_count_threshold,
>  					&format))
>      return NULL;
> @@ -702,7 +704,8 @@ valpy_format_string (PyObject *self, PyObject *args, PyObject *kw)
>      return NULL;
>  
>    /* Numeric arguments for which 0 means unlimited (which we represent as
> -     UINT_MAX).  */
> +     UINT_MAX).  Note that the max-depth numeric argument uses -1 as
> +     unlimited, and 0 is a valid choice.  */
>    if (opts.print_max == 0)
>      opts.print_max = UINT_MAX;
>    if (opts.repeat_count_threshold == 0)
> diff --git a/gdb/testsuite/gdb.base/max-depth.c b/gdb/testsuite/gdb.base/max-depth.c
> new file mode 100644
> index 00000000000..3c57249e699
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/max-depth.c
> @@ -0,0 +1,232 @@
> +/* 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/>.  */
> +
> +struct s1
> +{
> +  int x;
> +  int y;
> +} s1;
> +
> +struct s2
> +{
> +  int x;
> +  int y;
> +  struct
> +  {
> +    int z;
> +    int a;
> +  };
> +} s2;
> +
> +struct s3
> +{
> +  int x;
> +  int y;
> +  struct
> +  {
> +    int z;
> +    int a;
> +    struct
> +    {
> +      int b;
> +      int c;
> +    };
> +  };
> +} s3;
> +
> +struct s4
> +{
> +  int x;
> +  int y;
> +  struct
> +  {
> +    int x;
> +    int y;
> +    struct
> +    {
> +      int x;
> +      int y;
> +    } l2;
> +  } l1;
> +} s4;
> +
> +struct s5
> +{
> +  union
> +  {
> +    int raw[3];
> +    struct
> +    {
> +      int x;
> +      int y;
> +      int z;
> +    };
> +  };
> +} s5;
> +
> +typedef struct
> +{
> +  union
> +  {
> +    int raw[3];
> +    struct
> +    {
> +      int x;
> +      int y;
> +      int z;
> +    };
> +  };
> +} s6_t;
> +
> +s6_t s6;
> +
> +struct s7
> +{
> +  struct
> +  {
> +    int x;
> +    int y;
> +  };
> +  struct
> +  {
> +    int z;
> +    int a;
> +  };
> +  struct
> +  {
> +    int b;
> +    int c;
> +  };
> +} s7;
> +
> +struct s8
> +{
> +  int x;
> +  int y;
> +  struct
> +  {
> +    int z;
> +    int a;
> +    struct
> +    {
> +      int b;
> +      int c;
> +    };
> +  } d1;
> +} s8;
> +
> +struct s9
> +{
> +  int x;
> +  int y;
> +  struct
> +  {
> +    int k;
> +    int j;
> +    struct
> +    {
> +      int z;
> +      int a;
> +      struct
> +      {
> +        int b;
> +        int c;
> +      };
> +    } d1;
> +  };
> +  struct
> +  {
> +    int z;
> +    int a;
> +    struct
> +    {
> +      int b;
> +      int c;
> +    };
> +  } d2;
> +} s9;
> +
> +struct s10
> +{
> +  int x[10];
> +  int y;
> +  struct
> +  {
> +    int k[10];
> +    int j;
> +    struct
> +    {
> +      int z;
> +      int a;
> +      struct
> +      {
> +        int b[10];
> +        int c;
> +      };
> +    } d1;
> +  };
> +  struct
> +  {
> +    int z;
> +    int a;
> +    struct
> +    {
> +      int b[10];
> +      int c;
> +    };
> +  } d2;
> +} s10;
> +
> +struct s11
> +{
> +  int x;
> +  char s[10];
> +  struct
> +  {
> +    int z;
> +    int a;
> +  };
> +} s11;
> +
> +/* The following are C++ inheritance testing.  */
> +#ifdef __cplusplus
> +
> +/* This is non-virtual inheritance.  */
> +struct C1 { int c1 = 1; } c1;
> +struct C2 { int c2 = 2; } c2;
> +struct C3 : C2 { int c3 = 3; } c3;
> +struct C4 { int c4 = 4; } c4;
> +struct C5 : C4 { int c5 = 5; } c5;
> +struct C6 : C5 { int c6 = 6; } c6;
> +struct C7 : C1, C3, C6 { int c7 = 7; } c7;
> +
> +/* This is virtual inheritance.  */
> +struct V1 { int v1 = 1; } v1;
> +struct V2 : virtual V1 { int v2 = 2; } v2;
> +struct V3 : virtual V1 { int v3 = 3; } v3;
> +struct V4 : virtual V2 { int v4 = 4; } v4;
> +struct V5 : virtual V2 { int v5 = 1; } v5;
> +struct V6 : virtual V2, virtual V3 { int v6 = 1; } v6;
> +struct V7 : virtual V4, virtual V5, virtual V6 { int v7 = 1; } v7;
> +
> +#endif /* __cplusplus */
> +
> +int
> +main ()
> +{
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.base/max-depth.exp b/gdb/testsuite/gdb.base/max-depth.exp
> new file mode 100644
> index 00000000000..b3b78762eb1
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/max-depth.exp
> @@ -0,0 +1,165 @@
> +# 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/>.
> +
> +# Tests GDB's handling of 'set print max-depth'.
> +
> +# Only test C++ if we are able.  Always use C.
> +if { [skip_cplus_tests] || [get_compiler_info "c++"] } {
> +    set lang {c}
> +} else {
> +    set lang {c c++}
> +}
> +
> +foreach l $lang {
> +    set dir "$l"
> +    remote_exec host "rm -rf [standard_output_file ${dir}]"
> +    remote_exec host "mkdir -p [standard_output_file ${dir}]"
> +}
> +
> +proc compile_and_run_tests { lang } {
> +    global testfile
> +    global srcfile
> +    global binfile
> +    global hex
> +
> +    standard_testfile .c
> +
> +    # Create the additional flags.
> +    set flags "debug"
> +    lappend flags $lang
> +
> +    set dir "$lang"
> +    set binfile [standard_output_file ${dir}/${testfile}]
> +    if { [prepare_for_testing "failed to prepare" "${binfile}" "${srcfile}" "${flags}"] } {
> +	return 0
> +    }
> +
> +    # Advance to main.
> +    if { ![runto_main] } then {
> +	fail "can't run to main"
> +	return 0
> +    }
> +
> +    # The max-depth setting has no effect as the anonymous scopes are
> +    # ignored and the members are aggregated into the parent scope.
> +    gdb_print_expr_at_depths "s1" {"{...}" \
> +				       "{x = 0, y = 0}"\
> +				       "{x = 0, y = 0}"}
> +
> +    gdb_print_expr_at_depths "s2" {"{...}" \
> +				       "{x = 0, y = 0, {z = 0, a = 0}}" \
> +				       "{x = 0, y = 0, {z = 0, a = 0}}"}
> +
> +    gdb_print_expr_at_depths "s3" {"{...}" \
> +				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
> +				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}" \
> +				       "{x = 0, y = 0, {z = 0, a = 0, {b = 0, c = 0}}}"}
> +
> +    # Increasing max-depth unfurls more of the object.
> +    gdb_print_expr_at_depths "s4" {"{...}" \
> +				       "{x = 0, y = 0, l1 = {...}}" \
> +				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {...}}}" \
> +				       "{x = 0, y = 0, l1 = {x = 0, y = 0, l2 = {x = 0, y = 0}}}"}
> +
> +    # Check handling of unions, in this case 'raw' is printed instead of
> +    # just {...} as this is not useful.
> +    gdb_print_expr_at_depths "s5" {"{...}" \
> +				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
> +				       "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"}
> +
> +    # Check handling of typedefs.
> +    gdb_print_expr_at_depths "s6" {"{...}" \
> +				       "{{raw = {...}, {x = 0, y = 0, z = 0}}}" \
> +				       "{{raw = \\{0, 0, 0\\}, {x = 0, y = 0, z = 0}}}"}
> +
> +    # Multiple anonymous structures in parallel.
> +    gdb_print_expr_at_depths "s7" {"{...}" \
> +				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}" \
> +				       "{{x = 0, y = 0}, {z = 0, a = 0}, {b = 0, c = 0}}"}
> +
> +    # Flip flop between named and anonymous.  Expected to unfurl to the
> +    # first non-anonymous type.
> +    gdb_print_expr_at_depths "s8" {"{...}" \
> +				       "{x = 0, y = 0, d1 = {...}}" \
> +				       "{x = 0, y = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}"}
> +
> +    # Imbalanced tree, this will unfurl one size more than the other as
> +    # one side has more anonymous levels.
> +    gdb_print_expr_at_depths "s9" {"{...}" \
> +				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {...}}, d2 = {...}}" \
> +				       "{x = 0, y = 0, {k = 0, j = 0, d1 = {z = 0, a = 0, {b = 0, c = 0}}}, d2 = {z = 0, a = 0, {b = 0, c = 0}}}"}
> +
> +    # Arrays are treated as an extra level, while scalars are not.
> +    gdb_print_expr_at_depths "s10" {"{...}" \
> +					"{x = {...}, y = 0, {k = {...}, j = 0, d1 = {...}}, d2 = {...}}" \
> +					"{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = {...}, c = 0}}}, d2 = {z = 0, a = 0, {b = {...}, c = 0}}}" \
> +					"{x = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, y = 0, {k = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, j = 0, d1 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}, d2 = {z = 0, a = 0, {b = \\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\}, c = 0}}}"}
> +
> +    # Strings are treated as scalars.
> +    gdb_print_expr_at_depths "s11" {"{...}" \
> +					"{x = 0, s = \"\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\\\\000\", {z = 0, a = 0}}"}
> +
> +
> +    if { $lang == "c++" } {
> +	gdb_print_expr_at_depths "c1" {"{...}" \
> +					   "{c1 = 1}" }
> +	gdb_print_expr_at_depths "c2" { "{...}" "{c2 = 2}" }
> +	gdb_print_expr_at_depths "c3" { "{...}" \
> +					    "{<C2> = {...}, c3 = 3}" \
> +					    "{<C2> = {c2 = 2}, c3 = 3}" }
> +	gdb_print_expr_at_depths "c4" { "{...}" "{c4 = 4}" }
> +	gdb_print_expr_at_depths "c5" { "{...}" \
> +					    "{<C4> = {...}, c5 = 5}" \
> +					    "{<C4> = {c4 = 4}, c5 = 5}" }
> +	gdb_print_expr_at_depths "c6" { "{...}" \
> +					    "{<C5> = {...}, c6 = 6}" \
> +					    "{<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}" \
> +					    "{<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}" }
> +	gdb_print_expr_at_depths "c7" { "{...}" \
> +					    "{<C1> = {...}, <C3> = {...}, <C6> = {...}, c7 = 7}" \
> +					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {...}, c3 = 3}, <C6> = {<C5> = {...}, c6 = 6}, c7 = 7}" \
> +					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {...}, c5 = 5}, c6 = 6}, c7 = 7}" \
> +					    "{<C1> = {c1 = 1}, <C3> = {<C2> = {c2 = 2}, c3 = 3}, <C6> = {<C5> = {<C4> = {c4 = 4}, c5 = 5}, c6 = 6}, c7 = 7}" }
> +
> +	gdb_print_expr_at_depths "v1" [list "{...}" "{v1 = 1}" ]
> +	gdb_print_expr_at_depths "v2" [list "{...}" \
> +					    "{<V1> = {...}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" \
> +					    "{<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V2>, v2 = 2}" ]
> +	gdb_print_expr_at_depths "v3" [list "{...}" \
> +					    "{<V1> = {...}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" \
> +					    "{<V1> = {v1 = 1}, _vptr.V3 = $hex <VTT for V3>, v3 = 3}" ]
> +	gdb_print_expr_at_depths "v4" [list "{...}" \
> +					    "{<V2> = {...}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \
> +					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" \
> +					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V4>, v2 = 2}, _vptr.V4 = $hex <vtable for V4\[^>\]+>, v4 = 4}" ]
> +	gdb_print_expr_at_depths "v5" [list "{...}" \
> +					    "{<V2> = {...}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \
> +					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" \
> +					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <VTT for V5>, v2 = 2}, _vptr.V5 = $hex <vtable for V5\[^>\]+>, v5 = 1}" ]
> +	gdb_print_expr_at_depths "v6" [list "{...}" \
> +					    "{<V2> = {...}, <V3> = {...}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \
> +					    "{<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" \
> +					    "{<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V6\[^>\]+>, v2 = 2}, <V3> = {_vptr.V3 = $hex <VTT for V6>, v3 = 3}, _vptr.V6 = $hex <vtable for V6\[^>\]+>, v6 = 1}" ]
> +	gdb_print_expr_at_depths "v7" [list "{...}" \
> +					    "{<V4> = {...}, <V5> = {...}, <V6> = {...}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
> +					    "{<V4> = {<V2> = {...}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {...}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
> +					    "{<V4> = {<V2> = {<V1> = {...}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" \
> +					    "{<V4> = {<V2> = {<V1> = {v1 = 1}, _vptr.V2 = $hex <vtable for V7\[^>\]+>, v2 = 2}, _vptr.V4 = $hex <vtable for V7\[^>\]+>, v4 = 4}, <V5> = {_vptr.V5 = $hex <vtable for V7\[^>\]+>, v5 = 1}, <V6> = {<V3> = {_vptr.V3 = $hex <VTT for V7>, v3 = 3}, _vptr.V6 = $hex <vtable for V7\[^>\]+>, v6 = 1}, _vptr.V7 = $hex <vtable for V7\[^>\]+>, v7 = 1}" ]
> +    }
> +}
> +
> +foreach_with_prefix l $lang {
> +    compile_and_run_tests $l
> +}
> diff --git a/gdb/testsuite/gdb.fortran/max-depth.exp b/gdb/testsuite/gdb.fortran/max-depth.exp
> new file mode 100644
> index 00000000000..8b0bb8058fe
> --- /dev/null
> +++ b/gdb/testsuite/gdb.fortran/max-depth.exp
> @@ -0,0 +1,41 @@
> +# 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/>.
> +
> +# This file tests GDB's handling of 'set print max-depth' for nested
> +# fortran types.
> +
> +load_lib "fortran.exp"
> +
> +if { [skip_fortran_tests] } { continue }
> +
> +standard_testfile .f90
> +
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug f90}] } {
> +    return -1
> +}
> +
> +if { ![runto MAIN__] } {
> +    perror "Could not run to breakpoint `MAIN__'."
> +    continue
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "stop-here"]
> +gdb_continue_to_breakpoint "stop-here" ".*stop-here.*"
> +
> +gdb_print_expr_at_depths "var" {"\\(\\.\\.\\.\\)" \
> +				    "\\( d = \\(\\.\\.\\.\\) \\)" \
> +				    "\\( d = \\( string = 'abcde', c = \\(\\.\\.\\.\\) \\) \\)" \
> +				    "\\( d = \\( string = 'abcde', c = \\( b = \\(\\.\\.\\.\\), array = \\(\\.\\.\\.\\) \\) \\) \\)" \
> +				    "\\( d = \\( string = 'abcde', c = \\( b = \\( a = 1 \\), array = \\(0, 0, 0, 0, 0\\) \\) \\) \\)" }
> diff --git a/gdb/testsuite/gdb.fortran/max-depth.f90 b/gdb/testsuite/gdb.fortran/max-depth.f90
> new file mode 100644
> index 00000000000..9b6f90c2057
> --- /dev/null
> +++ b/gdb/testsuite/gdb.fortran/max-depth.f90
> @@ -0,0 +1,42 @@
> +! 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 2 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/>.
> +
> +program max_depth_test
> +  type :: s1
> +     integer :: a
> +  end type s1
> +
> +  type :: s2
> +     type (s1) :: b
> +     integer :: array (5)
> +  end type s2
> +
> +  type :: s3
> +     character(5) :: string
> +     type (s2) :: c
> +  end type s3
> +
> +  type :: s4
> +     type (s3) :: d
> +  end type s4
> +
> +  logical :: l
> +  type (s4) :: var
> +
> +  var%d%c%b%a = 1
> +  var%d%c%array = 0
> +  var%d%string = "abcde"
> +  l = .FALSE.					! stop-here
> +end program max_depth_test
> diff --git a/gdb/testsuite/gdb.go/max-depth.exp b/gdb/testsuite/gdb.go/max-depth.exp
> new file mode 100644
> index 00000000000..0f8ba9ab83f
> --- /dev/null
> +++ b/gdb/testsuite/gdb.go/max-depth.exp
> @@ -0,0 +1,40 @@
> +# 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/>.
> +
> +# Test the use of 'set prinnt max-depth' when printing go structures.
> +
> +load_lib "go.exp"
> +
> +if { [skip_go_tests] } { continue }
> +
> +standard_testfile .go
> +
> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {debug go}] } {
> +    return -1
> +}
> +
> +if { [go_runto_main] < 0 } {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "Break here"]
> +gdb_continue_to_breakpoint "run to breakpoint" ".*Break here.*"
> +
> +gdb_print_expr_at_depths "s1" [list "{...}" \
> +				    "{m_struct = {...}, m_string = $hex \"hello world\"}" \
> +				    "{m_struct = {m_a = 3, m_b = 7}, m_string = $hex \"hello world\"}"]
> diff --git a/gdb/testsuite/gdb.go/max-depth.go b/gdb/testsuite/gdb.go/max-depth.go
> new file mode 100644
> index 00000000000..6dbfc22b84d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.go/max-depth.go
> @@ -0,0 +1,41 @@
> +// 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/>.
> +
> +package main
> +
> +import "fmt"
> +
> +type S1 struct {
> +     m_a int
> +     m_b int
> +}
> +
> +type S2 struct {
> +     m_struct S1
> +     m_string string
> +}
> +
> +func main () {
> +  var s1 S2
> +  var i int
> +
> +  s1.m_string = "hello world"
> +  s1.m_struct.m_a = 3
> +  s1.m_struct.m_b = 7
> +
> +  i = 0           // Break here.
> +  fmt.Println ("Got: %d, %d, %d, %s", i, s1.m_struct.m_a,
> +               s1.m_struct.m_b, s1.m_string)
> +}
> diff --git a/gdb/testsuite/gdb.modula2/max-depth.c b/gdb/testsuite/gdb.modula2/max-depth.c
> new file mode 100644
> index 00000000000..c2fd70ca632
> --- /dev/null
> +++ b/gdb/testsuite/gdb.modula2/max-depth.c
> @@ -0,0 +1,45 @@
> +/* 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/>.  */
> +
> +#include <string.h>
> +
> +struct S1
> +{
> +  int a;
> +  int b;
> +};
> +
> +struct S2
> +{
> +  char str[10];
> +  struct S1 s1;
> +};
> +
> +struct S3
> +{
> +  struct S2 s2;
> +};
> +
> +int
> +main ()
> +{
> +  struct S3 s3;
> +
> +  memcpy (s3.s2.str, "abcde\0fghi", 10);
> +  s3.s2.s1.a = 3;
> +  s3.s2.s1.b = 4;
> +
> +  return 0;	/* Break here.  */
> +}
> diff --git a/gdb/testsuite/gdb.modula2/max-depth.exp b/gdb/testsuite/gdb.modula2/max-depth.exp
> new file mode 100644
> index 00000000000..ac7edfe8f72
> --- /dev/null
> +++ b/gdb/testsuite/gdb.modula2/max-depth.exp
> @@ -0,0 +1,37 @@
> +# 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/>.
> +
> +# Test the use of 'set prinnt max-depth' when printing modula2 structures.
> +
> +standard_testfile
> +
> +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug quiet}]} {
> +    return -1
> +}
> +
> +if { ![runto_main] } then {
> +    fail "can't run to main"
> +    return -1
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "Break here"]
> +gdb_continue_to_breakpoint "breakpoint"
> +
> +gdb_test "set lang modula-2" ".*does not match.*" "switch to modula-2"
> +
> +gdb_print_expr_at_depths "s3" [list "{...}" \
> +				    "{s2 = {...}}" \
> +				    "{s2 = {str = \"abcde\\\\000fghi\", s1 = {...}}}" \
> +				    "{s2 = {str = \"abcde\\\\000fghi\", s1 = {a = 3, b = 4}}}"]
> diff --git a/gdb/testsuite/gdb.python/py-format-string.exp b/gdb/testsuite/gdb.python/py-format-string.exp
> index 2f574fbfa5a..ebb2074ce87 100644
> --- a/gdb/testsuite/gdb.python/py-format-string.exp
> +++ b/gdb/testsuite/gdb.python/py-format-string.exp
> @@ -672,6 +672,26 @@ proc test_max_elements {} {
>    }
>  }
>  
> +# Test the max_depth option for gdb.Value.format_string.
> +proc test_max_depth {} {
> +    set opts "max_depth=-1"
> +    with_test_prefix $opts {
> +	check_format_string "a_struct_with_union" $opts
> +    }
> +    set opts "max_depth=0"
> +    with_test_prefix $opts {
> +	check_format_string "a_struct_with_union" $opts "\\{\.\.\.\\}"
> +    }
> +    set opts "max_depth=1"
> +    with_test_prefix $opts {
> +	check_format_string "a_struct_with_union" $opts "\\{the_union = \\{\.\.\.\\}\\}"
> +    }
> +    set opts "max_depth=2"
> +    with_test_prefix $opts {
> +	check_format_string "a_struct_with_union" $opts
> +    }
> +}
> +
>  # Test the repeat_threshold option for gdb.Value.format_string.
>  proc test_repeat_threshold {} {
>    global current_lang
> @@ -925,6 +945,7 @@ proc test_all_common {} {
>    test_actual_objects
>    test_static_members
>    test_max_elements
> +  test_max_depth
>    test_repeat_threshold
>    test_format
>    # Multiple options mixed together.
> diff --git a/gdb/testsuite/gdb.python/py-nested-maps.c b/gdb/testsuite/gdb.python/py-nested-maps.c
> new file mode 100644
> index 00000000000..94107c0180d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-nested-maps.c
> @@ -0,0 +1,130 @@
> +/* 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/>.  */
> +
> +#include <stdlib.h>
> +#include <string.h>
> +
> +#define FIXED_MAP_SIZE 10
> +
> +struct key_t
> +{
> +  int a;
> +  int b;
> +};
> +
> +struct value_t
> +{
> +  int x;
> +  int y;
> +  int z;
> +};
> +
> +struct map_t
> +{
> +  const char *name;
> +  int length;
> +  struct key_t *keys;
> +  struct value_t *values;
> +
> +  /* This field is used only by the pretty printer.  */
> +  int show_header;
> +};
> +
> +struct map_map_t
> +{
> +  int length;
> +  struct map_t **values;
> +
> +  /* This field is used only by the pretty printer.  */
> +  int show_header;
> +};
> +
> +struct map_t *
> +create_map (const char *name)
> +{
> +  struct map_t *m = malloc (sizeof (struct map_t));
> +  m->name = strdup (name);
> +  m->length = 0;
> +  m->keys = NULL;
> +  m->values = NULL;
> +  m->show_header = 0;
> +}
> +
> +void
> +add_map_element (struct map_t *m, struct key_t k, struct value_t v)
> +{
> +  if (m->length == 0)
> +    {
> +      m->keys = malloc (sizeof (struct key_t) * FIXED_MAP_SIZE);
> +      m->values = malloc (sizeof (struct value_t) * FIXED_MAP_SIZE);
> +    }
> +
> +  m->keys[m->length] = k;
> +  m->values[m->length] = v;
> +  m->length++;
> +}
> +
> +struct map_map_t *
> +create_map_map (void)
> +{
> +  struct map_map_t *mm = malloc (sizeof (struct map_map_t));
> +  mm->length = 0;
> +  mm->values = NULL;
> +  mm->show_header = 0;
> +}
> +
> +void
> +add_map_map_element (struct map_map_t *mm, struct map_t *map)
> +{
> +  if (mm->length == 0)
> +    mm->values = malloc (sizeof (struct map_t *) * FIXED_MAP_SIZE);
> +
> +  mm->values[mm->length] = map;
> +  mm->length++;
> +}
> +
> +int
> +main (void)
> +{
> +  struct map_t *m1 = create_map ("m1");
> +  struct key_t k1 = {3, 4};
> +  struct key_t k2 = {4, 5};
> +  struct key_t k3 = {5, 6};
> +  struct key_t k4 = {6, 7};
> +  struct key_t k5 = {7, 8};
> +  struct key_t k6 = {8, 9};
> +  struct value_t v1 = {0, 1, 2};
> +  struct value_t v2 = {3, 4, 5};
> +  struct value_t v3 = {6, 7, 8};
> +  struct value_t v4 = {9, 0, 1};
> +  struct value_t v5 = {2, 3, 4};
> +  struct value_t v6 = {5, 6, 7};
> +  add_map_element (m1, k1, v1);
> +  add_map_element (m1, k2, v2);
> +  add_map_element (m1, k3, v3);
> +
> +  struct map_t *m2 = create_map ("m2");
> +  add_map_element (m2, k4, v4);
> +  add_map_element (m2, k5, v5);
> +  add_map_element (m2, k6, v6);
> +
> +  struct map_map_t *mm = create_map_map ();
> +  add_map_map_element (mm, m1);
> +  add_map_map_element (mm, m2);
> +
> +  return 0; /* Break here.  */
> +}
> diff --git a/gdb/testsuite/gdb.python/py-nested-maps.exp b/gdb/testsuite/gdb.python/py-nested-maps.exp
> new file mode 100644
> index 00000000000..81dd99ca2c9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-nested-maps.exp
> @@ -0,0 +1,238 @@
> +# Copyright (C) 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 tests GDB's python pretty printing of nested map like
> +# structures using structures as keys and values, it then tests how
> +# 'set print max-depth' affects this printing.
> +
> +load_lib gdb-python.exp
> +
> +standard_testfile
> +
> +# Start with a fresh gdb.
> +gdb_exit
> +gdb_start
> +
> +# Skip all tests if Python scripting is not enabled.
> +if { [skip_python_tests] } { continue }
> +
> +standard_testfile
> +
> +if { [prepare_for_testing "failed to prepare" $testfile $srcfile {debug}] } {
> +    return -1
> +}
> +
> +if ![runto_main ] then {
> +    return -1
> +}
> +
> +gdb_breakpoint [gdb_get_line_number "Break here"]
> +gdb_continue_to_breakpoint "run to testing point" ".*Break here.*"
> +
> +set remote_python_file [gdb_remote_download host \
> +			    ${srcdir}/${subdir}/${testfile}.py]
> +gdb_test_no_output "source ${remote_python_file}" "load python file"
> +
> +# Test printing with 'set print pretty off'.
> +gdb_test_no_output "set print pretty off"
> +with_test_prefix "pretty=off" {
> +    gdb_print_expr_at_depths "*m1" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     "\{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
> +	     "\{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
> +	    ]
> +
> +    gdb_print_expr_at_depths "*mm" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
> +	     "\{\\\[$hex \"m1\"\\\] = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
> +	    ]
> +}
> +
> +# Now again, but with 'set print pretty on'.
> +gdb_test_no_output "set print pretty on"
> +with_test_prefix "pretty=on" {
> +    gdb_print_expr_at_depths "*m1" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     [multi_line \
> +		  " = \{" \
> +		  "  \\\[\{" \
> +		  "    a = 3," \
> +		  "    b = 4" \
> +		  "  \}\\\] = \{\\.\\.\\.\}," \
> +		  "  \\\[\{" \
> +		  "    a = 4," \
> +		  "    b = 5" \
> +		  "  \}\\\] = \{\\.\\.\\.\}," \
> +		  "  \\\[\{" \
> +		  "    a = 5," \
> +		  "    b = 6" \
> +		  "  \}\\\] = \{\\.\\.\\.\}" \
> +		  "\}" ] \
> +	     [multi_line \
> +		  " = \{" \
> +		  "  \\\[\{" \
> +		  "    a = 3," \
> +		  "    b = 4" \
> +		  "  \}\\\] = \{" \
> +		  "    x = 0," \
> +		  "    y = 1," \
> +		  "    z = 2" \
> +		  "  \}," \
> +		  "  \\\[\{" \
> +		  "    a = 4," \
> +		  "    b = 5" \
> +		  "  \}\\\] = \{" \
> +		  "    x = 3," \
> +		  "    y = 4," \
> +		  "    z = 5" \
> +		  "  \}," \
> +		  "  \\\[\{" \
> +		  "    a = 5," \
> +		  "    b = 6" \
> +		  "  \}\\\] = \{" \
> +		  "    x = 6," \
> +		  "    y = 7," \
> +		  "    z = 8" \
> +		  "  \}" \
> +		  "\}" ] \
> +	    ]
> +
> +     gdb_print_expr_at_depths "*mm" \
> +	[list \
> +	     "\{\\.\\.\\.\}" \
> +	     [multi_line \
> +		  " = \{" \
> +		  "  \\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}," \
> +		  "  \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}" \
> +		  "\}" ] \
> +	     [multi_line \
> +		  " = \{" \
> +		  "  \\\[$hex \"m1\"\\\] = \{" \
> +		  "    \\\[\{" \
> +		  "      a = 3," \
> +		  "      b = 4" \
> +		  "    \}\\\] = \{\\.\\.\\.\}," \
> +		  "    \\\[\{" \
> +		  "      a = 4," \
> +		  "      b = 5" \
> +		  "    \}\\\] = \{\\.\\.\\.\}," \
> +		  "    \\\[\{" \
> +		  "      a = 5," \
> +		  "      b = 6" \
> +		  "    \}\\\] = \{\\.\\.\\.\}" \
> +		  "  \}," \
> +		  "  \\\[$hex \"m2\"\\\] = \{" \
> +		  "    \\\[\{" \
> +		  "      a = 6," \
> +		  "      b = 7" \
> +		  "    \}\\\] = \{\\.\\.\\.\}," \
> +		  "    \\\[\{" \
> +		  "      a = 7," \
> +		  "      b = 8" \
> +		  "    \}\\\] = \{\\.\\.\\.\}," \
> +		  "    \\\[\{" \
> +		  "      a = 8," \
> +		  "      b = 9" \
> +		  "    \}\\\] = \{\\.\\.\\.\}" \
> +		  "  \}" \
> +		  "\}" ] \
> +	     [multi_line \
> +		  " = \{" \
> +		  "  \\\[$hex \"m1\"\\\] = \{" \
> +		  "    \\\[\{" \
> +		  "      a = 3," \
> +		  "      b = 4" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 0," \
> +		  "      y = 1," \
> +		  "      z = 2" \
> +		  "    \}," \
> +		  "    \\\[\{" \
> +		  "      a = 4," \
> +		  "      b = 5" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 3," \
> +		  "      y = 4," \
> +		  "      z = 5" \
> +		  "    \}," \
> +		  "    \\\[\{" \
> +		  "      a = 5," \
> +		  "      b = 6" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 6," \
> +		  "      y = 7," \
> +		  "      z = 8" \
> +		  "    \}" \
> +		  "  \}," \
> +		  "  \\\[$hex \"m2\"\\\] = \{" \
> +		  "    \\\[\{" \
> +		  "      a = 6," \
> +		  "      b = 7" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 9," \
> +		  "      y = 0," \
> +		  "      z = 1" \
> +		  "    \}," \
> +		  "    \\\[\{" \
> +		  "      a = 7," \
> +		  "      b = 8" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 2," \
> +		  "      y = 3," \
> +		  "      z = 4" \
> +		  "    \}," \
> +		  "    \\\[\{" \
> +		  "      a = 8," \
> +		  "      b = 9" \
> +		  "    \}\\\] = \{" \
> +		  "      x = 5," \
> +		  "      y = 6," \
> +		  "      z = 7" \
> +		  "    \}" \
> +		  "  \}" \
> +		  "\}" ] \
> +	    ]
> +}
> +
> +# Test printing with 'set print pretty off', but this time display a
> +# top level string (returned from the to_string method) as part of the
> +# printed value.
> +gdb_test_no_output "set mm->show_header=1"
> +gdb_test_no_output "set m1->show_header=1"
> +gdb_test_no_output "set m2->show_header=1"
> +with_test_prefix "headers=on" {
> +    gdb_test_no_output "set print pretty off"
> +    with_test_prefix "pretty=off" {
> +	gdb_print_expr_at_depths "*m1" \
> +	    [list \
> +		 "\{\\.\\.\\.\}" \
> +		 "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}" \
> +		 "pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}" \
> +		]
> +
> +	gdb_print_expr_at_depths "*mm" \
> +	    [list \
> +		 "\{\\.\\.\\.\}" \
> +		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = \{\\.\\.\\.\}, \\\[$hex \"m2\"\\\] = \{\\.\\.\\.\}\}" \
> +		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 4, b = 5\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 5, b = 6\}\\\] = \{\\.\\.\\.\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 7, b = 8\}\\\] = \{\\.\\.\\.\}, \\\[\{a = 8, b = 9\}\\\] = \{\\.\\.\\.\}\}\}" \
> +		 "pp_map_map = \{\\\[$hex \"m1\"\\\] = pp_map = \{\\\[\{a = 3, b = 4\}\\\] = \{x = 0, y = 1, z = 2\}, \\\[\{a = 4, b = 5\}\\\] = \{x = 3, y = 4, z = 5\}, \\\[\{a = 5, b = 6\}\\\] = \{x = 6, y = 7, z = 8\}\}, \\\[$hex \"m2\"\\\] = pp_map = \{\\\[\{a = 6, b = 7\}\\\] = \{x = 9, y = 0, z = 1\}, \\\[\{a = 7, b = 8\}\\\] = \{x = 2, y = 3, z = 4\}, \\\[\{a = 8, b = 9\}\\\] = \{x = 5, y = 6, z = 7\}\}\}" \
> +		]
> +    }
> +}
> diff --git a/gdb/testsuite/gdb.python/py-nested-maps.py b/gdb/testsuite/gdb.python/py-nested-maps.py
> new file mode 100644
> index 00000000000..d3fdf59533d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.python/py-nested-maps.py
> @@ -0,0 +1,135 @@
> +# Copyright (C) 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 GDB's printing of
> +# nested map like structures.
> +
> +import re
> +import gdb
> +
> +def _iterator1 (pointer, len):
> +    while len > 0:
> +        map = pointer.dereference()
> +        yield ('', map['name'])
> +        yield ('', map.dereference())
> +        pointer += 1
> +        len -= 1
> +
> +def _iterator2 (pointer1, pointer2, len):
> +    while len > 0:
> +        yield ("", pointer1.dereference())
> +        yield ("", pointer2.dereference())
> +        pointer1 += 1
> +        pointer2 += 1
> +        len -= 1
> +
> +class pp_map (object):
> +    def __init__(self, val):
> +        self.val = val
> +
> +    def to_string(self):
> +        if (self.val['show_header'] == 0):
> +            return None
> +        else:
> +            return "pp_map"
> +
> +    def children(self):
> +        return _iterator2(self.val['keys'],
> +                          self.val['values'],
> +                          self.val['length'])
> +
> +    def display_hint (self):
> +        return 'map'
> +
> +class pp_map_map (object):
> +    def __init__(self, val):
> +        self.val = val
> +
> +    def to_string(self):
> +        if (self.val['show_header'] == 0):
> +            return None
> +        else:
> +            return "pp_map_map"
> +
> +    def children(self):
> +        return _iterator1(self.val['values'],
> +                          self.val['length'])
> +
> +    def display_hint (self):
> +        return 'map'
> +
> +def 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 == None:
> +        return None
> +
> +    # Iterate over local dictionary of types to determine
> +    # if a printer is registered for that type.  Return an
> +    # instantiation of the printer if found.
> +    for function in pretty_printers_dict:
> +        if function.match (typename):
> +            return pretty_printers_dict[function] (val)
> +
> +    # Cannot find a pretty printer.  Return None.
> +    return None
> +
> +# Lookup a printer for VAL in the typedefs dict.
> +def lookup_typedefs_function (val):
> +    "Look-up and return a pretty-printer that can print val (typedefs)."
> +
> +    # Get the type.
> +    type = val.type
> +
> +    if type == None or type.name == None or type.code != gdb.TYPE_CODE_TYPEDEF:
> +        return None
> +
> +    # Iterate over local dictionary of typedef types to determine if a
> +    # printer is registered for that type.  Return an instantiation of
> +    # the printer if found.
> +    for function in typedefs_pretty_printers_dict:
> +        if function.match (type.name):
> +            return typedefs_pretty_printers_dict[function] (val)
> +
> +    # Cannot find a pretty printer.
> +    return None
> +
> +def register_pretty_printers ():
> +    pretty_printers_dict[re.compile ('^struct map_t$')] = pp_map
> +    pretty_printers_dict[re.compile ('^map_t$')] = pp_map
> +    pretty_printers_dict[re.compile ('^struct map_map_t$')] = pp_map_map
> +    pretty_printers_dict[re.compile ('^map_map_t$')] = pp_map_map
> +
> +# Dict for struct types with typedefs fully stripped.
> +pretty_printers_dict = {}
> +# Dict for typedef types.
> +typedefs_pretty_printers_dict = {}
> +
> +register_pretty_printers ()
> +gdb.pretty_printers.append (lookup_function)
> +gdb.pretty_printers.append (lookup_typedefs_function)
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 6c0f243eef8..70e5dd0d540 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -1390,6 +1390,36 @@ proc gdb_test_stdio {command inferior_pattern {gdb_pattern ""} {message ""}} {
>      return $res
>  }
>  
> +# get_print_expr_at_depths EXP OUTPUTS
> +#
> +# Used for testing 'set print max-depth'.  Prints the expression EXP
> +# with 'set print max-depth' set to various depths.  OUTPUTS is a list
> +# of `n` different patterns to match at each of the depths from 0 to
> +# (`n` - 1).
> +#
> +# This proc does one final check with the max-depth set to 'unlimited'
> +# which is tested against the last pattern in the OUTPUTS list.  The
> +# OUTPUTS list is therefore required to match every depth from 0 to a
> +# depth where the whole of EXP is printed with no ellipsis.
> +#
> +# This proc leaves the 'set print max-depth' set to 'unlimited'.
> +proc gdb_print_expr_at_depths {exp outputs} {
> +    for { set depth 0 } { $depth <= [llength $outputs] } { incr depth } {
> +	if { $depth == [llength $outputs] } {
> +	    set expected_result [lindex $outputs [expr [llength $outputs] - 1]]
> +	    set depth_string "unlimited"
> +	} else {
> +	    set expected_result [lindex $outputs $depth]
> +	    set depth_string $depth
> +	}
> +
> +	with_test_prefix "exp='$exp': depth=${depth_string}" {
> +	    gdb_test_no_output "set print max-depth ${depth_string}"
> +	    gdb_test "p $exp" "$expected_result"
> +	}
> +    }
> +}
> +
>  \f
>  
>  # Issue a PASS and return true if evaluating CONDITION in the caller's
> diff --git a/gdb/valprint.c b/gdb/valprint.c
> index b02ebf6c272..0447c715249 100644
> --- a/gdb/valprint.c
> +++ b/gdb/valprint.c
> @@ -88,6 +88,7 @@ static void val_print_type_code_flags (struct type *type,
>  				       struct ui_file *stream);
>  
>  #define PRINT_MAX_DEFAULT 200	/* Start print_max off at this value.  */
> +#define PRINT_MAX_DEPTH_DEFAULT 20	/* Start print_max_depth off at this value. */
>  
>  struct value_print_options user_print_options =
>  {
> @@ -109,7 +110,8 @@ struct value_print_options user_print_options =
>    1,				/* pascal_static_field_print */
>    0,				/* raw */
>    0,				/* summary */
> -  1				/* symbol_print */
> +  1,				/* symbol_print */
> +  PRINT_MAX_DEPTH_DEFAULT	/* max_depth */
>  };
>  
>  /* Initialize *OPTS to be a copy of the user print options.  */
> @@ -281,6 +283,18 @@ val_print_scalar_type_p (struct type *type)
>      }
>  }
>  
> +/* A helper function for val_print.  When printing with limited depth we
> +   want to print string and scalar arguments, but not aggregate arguments.
> +   This function distinguishes between the two.  */
> +
> +static bool
> +val_print_scalar_or_string_type_p (struct type *type,
> +				   const struct language_defn *language)
> +{
> +  return (val_print_scalar_type_p (type)
> +	  || language->la_is_string_type_p (type));
> +}
> +
>  /* See its definition in value.h.  */
>  
>  int
> @@ -1054,6 +1068,11 @@ val_print (struct type *type, LONGEST embedded_offset,
>        return;
>      }
>  
> +  /* If this value is too deep then don't print it.  */
> +  if (!val_print_scalar_or_string_type_p (type, language)
> +      && val_print_check_max_depth (stream, recurse, options, language))
> +    return;
> +
>    try
>      {
>        language->la_val_print (type, embedded_offset, address,
> @@ -1066,6 +1085,22 @@ val_print (struct type *type, LONGEST embedded_offset,
>      }
>  }
>  
> +/* See valprint.h.  */
> +
> +bool val_print_check_max_depth (struct ui_file *stream, int recurse,
> +				const struct value_print_options *options,
> +				const struct language_defn *language)
> +{
> +  if (options->max_depth > -1 && recurse >= options->max_depth)
> +    {
> +      gdb_assert (language->la_struct_too_deep_ellipsis != NULL);
> +      fputs_filtered (language->la_struct_too_deep_ellipsis, stream);
> +      return true;
> +    }
> +
> +  return false;
> +}
> +
>  /* Check whether the value VAL is printable.  Return 1 if it is;
>     return 0 and print an appropriate error message to STREAM according to
>     OPTIONS if it is not.  */
> @@ -2859,6 +2894,15 @@ val_print_string (struct type *elttype, const char *encoding,
>  
>    return (bytes_read / width);
>  }
> +
> +/* Handle 'show print max-depth'.  */
> +
> +static void
> +show_print_max_depth (struct ui_file *file, int from_tty,
> +		      struct cmd_list_element *c, const char *value)
> +{
> +  fprintf_filtered (file, _("Maximum print depth is %s.\n"), value);
> +}
>  \f
>  
>  /* The 'set input-radix' command writes to this auxiliary variable.
> @@ -3152,4 +3196,14 @@ Use 'show input-radix' or 'show output-radix' to independently show each."),
>  Set printing of array indexes."), _("\
>  Show printing of array indexes"), NULL, NULL, show_print_array_indexes,
>                             &setprintlist, &showprintlist);
> +
> +  add_setshow_zuinteger_unlimited_cmd ("max-depth", class_support,
> +                            &user_print_options.max_depth, _("\
> +Set maximum print depth for nested structures, unions and arrays."), _("\
> +Show maximum print depth for nested structures, unions, and arrays."), _("\
> +When structures, unions, or arrays are nested beyond this depth then they\n\
> +will be replaced with either '{...}' or '(...)' depending on the language.\n\
> +Use 'set print max-depth unlimited' to print the complete structure."),
> +				       NULL, show_print_max_depth,
> +				       &setprintlist, &showprintlist);
>  }
> diff --git a/gdb/valprint.h b/gdb/valprint.h
> index db99b52b2ad..e5cc9477987 100644
> --- a/gdb/valprint.h
> +++ b/gdb/valprint.h
> @@ -92,6 +92,9 @@ struct value_print_options
>    /* If nonzero, when printing a pointer, print the symbol to which it
>       points, if any.  */
>    int symbol_print;
> +
> +  /* Maximum print depth when printing nested aggregates.  */
> +  int max_depth;
>  };
>  
>  /* The global print options set by the user.  In general this should
> @@ -246,4 +249,13 @@ extern int build_address_symbolic (struct gdbarch *,
>  				   int *line,
>  				   int *unmapped);
>  
> +/* Check to see if RECURSE is greater than or equal to the allowed
> +   printing max-depth (see 'set print max-depth').  If it is then print an
> +   ellipsis expression to STREAM and return true, otherwise return false.
> +   LANGUAGE determines what type of ellipsis expression is printed.  */
> +
> +extern bool val_print_check_max_depth (struct ui_file *stream, int recurse,
> +				       const struct value_print_options *opts,
> +				       const struct language_defn *language);
> +
>  #endif

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
  2019-04-17  7:42     ` Philippe Waroquiers
@ 2019-04-17 14:35     ` Eli Zaretskii
  2019-04-18  1:07       ` Andrew Burgess
  2019-04-18 17:08     ` Pedro Alves
  2 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2019-04-17 14:35 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: Andrew Burgess <andrew.burgess@embecosm.com>
> Date: Wed, 17 Apr 2019 00:06:10 +0100
> 
> Introduce a new print setting max-depth which can be set with 'set
> print max-depth DEPTH'.  The default value of DEPTH is 20, but this
> can also be set to unlimited.

OK for the documentation parts, with one minor comment:

> +To see the contents of structures that have been hidden the user can
> +either increase the print max-depth to increase, or they can print

"increase ... to increase" sounds awkward and perhaps even confusing.
Can we come up with a better wording?

Thanks.

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

* Re: [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool
  2019-04-16 23:06   ` [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool Andrew Burgess
@ 2019-04-18  0:03     ` Joel Brobecker
  0 siblings, 0 replies; 33+ messages in thread
From: Joel Brobecker @ 2019-04-18  0:03 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

Hi Andrew,

On Wed, Apr 17, 2019 at 12:06:06AM +0100, Andrew Burgess wrote:
> A later commit would like to make use of a pointer to the function
> ada_is_string_type, however, this will require the function to return
> a bool (so the signature matches).
> 
> As the ada_is_string_type is a predicate function, and its return
> value is only ever used as either true or false, then this commit
> updates the function to return a bool.
> 
> As a consequence ada_is_character_type needs to change too.
> 
> There should be no user visible changes after this commit.
> 
> gdb/ChangeLog:
> 
> 	* ada-lang.c (ada_is_character_type): Change return type to bool.
> 	(ada_is_string_type): Likewise.
> 	* ada-lang.h (ada_is_character_type): Update declaration
> 	(ada_is_string_type): Likewise.

This a good change, so good for me!


-- 
Joel

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-17 14:35     ` Eli Zaretskii
@ 2019-04-18  1:07       ` Andrew Burgess
  2019-04-18  2:38         ` Eli Zaretskii
  0 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-18  1:07 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

* Eli Zaretskii <eliz@gnu.org> [2019-04-17 17:35:27 +0300]:

> > From: Andrew Burgess <andrew.burgess@embecosm.com>
> > Cc: Andrew Burgess <andrew.burgess@embecosm.com>
> > Date: Wed, 17 Apr 2019 00:06:10 +0100
> > 
> > Introduce a new print setting max-depth which can be set with 'set
> > print max-depth DEPTH'.  The default value of DEPTH is 20, but this
> > can also be set to unlimited.
> 
> OK for the documentation parts, with one minor comment:
> 
> > +To see the contents of structures that have been hidden the user can
> > +either increase the print max-depth to increase, or they can print
> 
> "increase ... to increase" sounds awkward and perhaps even confusing.
> Can we come up with a better wording?

You're right, that doesn't read well, how about simply dropping the
'...to increase' like this:


    +To see the contents of structures that have been hidden the user
    +can either increase the print max-depth, or they can print the
    +elements of the structure that are visible, for example
    +
    +@smallexample
    +(gdb) p var
    +$1 = @{d = @{c = @{...@}@}@}
    +(gdb) p var.d
    +$2 = @{c = @{b = @{...@}@}@}
    +(gdb) p var.d.c
    +$3 = @{b = @{a = 3@}@}
    +@end smallexample
    +

Thanks,
Andrew


> 
> Thanks.

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-18  1:07       ` Andrew Burgess
@ 2019-04-18  2:38         ` Eli Zaretskii
  2019-04-18 21:52           ` Andrew Burgess
  0 siblings, 1 reply; 33+ messages in thread
From: Eli Zaretskii @ 2019-04-18  2:38 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Date: Thu, 18 Apr 2019 02:07:26 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: gdb-patches@sourceware.org
> 
> > "increase ... to increase" sounds awkward and perhaps even confusing.
> > Can we come up with a better wording?
> 
> You're right, that doesn't read well, how about simply dropping the
> '...to increase' like this:
> 
> 
>     +To see the contents of structures that have been hidden the user
>     +can either increase the print max-depth, or they can print the
>     +elements of the structure that are visible, for example

Fine with me, thanks.

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-16 23:06   ` [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p Andrew Burgess
@ 2019-04-18 17:08     ` Pedro Alves
  2019-04-19 21:00       ` Andrew Burgess
  2019-04-19 14:45     ` Tom Tromey
  1 sibling, 1 reply; 33+ messages in thread
From: Pedro Alves @ 2019-04-18 17:08 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

On 4/17/19 12:06 AM, Andrew Burgess wrote:
> This commit is preparation work for the next commit, and by itself
> makes no user visible change to GDB.  I've split this work into a
> separate commit in order to make code review easier.
> 
> This commit adds a new field 'la_is_string_type_p' to the language
> struct, this predicate will return true if a type is a string type for
> the given language.
> 
> Some languages already have a "is this a string" predicate that I was
> able to reuse, while for other languages I've had to add a new
> predicate.  In this case I took inspiration from the value printing
> code for that language - what different conditions would result in
> printing something as a string.
> 
> A default "is this a string" method has also been added that looks for
> TYPE_CODE_STRING, this is the fallback I've used for a couple of
> languages.
> 
> In this commit I add the new field and initialise it for each
> language, however at this stage the new field is never used.
> 

Thanks.  This nicely addresses one of my previous review comments.

I can't speak to whether the implementation for the different languages
are correct.  E.g., I'm curious from where you extracted the M2 and
the Rust bits.  Didn't seem like it was a refactoring job?

Formatting comments below.

> +/* Return true if TYPE is a string.  */
> +static bool

Empty line after comment.

> +static bool
> +m2_is_string_type_p (struct type *type)
> +{
> +  type = check_typedef (type);
> +  if (TYPE_CODE (type) == TYPE_CODE_ARRAY
> +      && TYPE_LENGTH (type) > 0
> +      && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
> +    {
> +      struct type *elttype = check_typedef (TYPE_TARGET_TYPE (type));
> +
> +      if (TYPE_LENGTH (elttype) == 1 &&

&& goes on the next line.

> +	  (TYPE_CODE (elttype) == TYPE_CODE_INT
> +	   || TYPE_CODE (elttype) == TYPE_CODE_CHAR))
> +	return true;
> +    }
> +
> +  return false;
> +}
> +

Thanks,
Pedro Alves

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
  2019-04-17  7:42     ` Philippe Waroquiers
  2019-04-17 14:35     ` Eli Zaretskii
@ 2019-04-18 17:08     ` Pedro Alves
  2 siblings, 0 replies; 33+ messages in thread
From: Pedro Alves @ 2019-04-18 17:08 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Excellent.  I have no further comments on this version, except for ...

On 4/17/19 12:06 AM, Andrew Burgess wrote:

> +/* See valprint.h.  */
> +
> +bool val_print_check_max_depth (struct ui_file *stream, int recurse,
> +				const struct value_print_options *options,
> +				const struct language_defn *language)


... line break after the bool.

Thanks,
Pedro Alves

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-18  2:38         ` Eli Zaretskii
@ 2019-04-18 21:52           ` Andrew Burgess
  2019-04-19  6:50             ` Eli Zaretskii
  0 siblings, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-18 21:52 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, Philippe Waroquiers

* Eli Zaretskii <eliz@gnu.org> [2019-04-18 05:38:11 +0300]:

> > Date: Thu, 18 Apr 2019 02:07:26 +0100
> > From: Andrew Burgess <andrew.burgess@embecosm.com>
> > Cc: gdb-patches@sourceware.org
> > 
> > > "increase ... to increase" sounds awkward and perhaps even confusing.
> > > Can we come up with a better wording?
> > 
> > You're right, that doesn't read well, how about simply dropping the
> > '...to increase' like this:
> > 
> > 
> >     +To see the contents of structures that have been hidden the user
> >     +can either increase the print max-depth, or they can print the
> >     +elements of the structure that are visible, for example
> 
> Fine with me, thanks.

Eli, thanks for the feedback.

Having taken Philippe's feedback on board too, I have now adjusted how
I show the effects of the different depth settings to use a table
instead of a set of paragraphs.

I just wanted to run this by you to make sure I hadn't made any silly
style mistakes.

The diff below is only for gdb/doc/gdb.texinfo - this is the only file
I've changed over the previous version of the patch.

thanks,
Andrew

--

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f410d026b82..02e90284494 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -10560,6 +10560,64 @@
 Display the current threshold for printing repeated identical
 elements.
 
+@item set print max-depth @var{depth}
+@item set print max-depth unlimited
+@cindex printing nested structures
+Set the threshold after which nested structures are replaced with
+ellipsis, this can make visualising deeply nested structures easier.
+
+For example, given this C code
+
+@smallexample
+typedef struct s1 @{ int a; @} s1;
+typedef struct s2 @{ s1 b; @} s2;
+typedef struct s3 @{ s2 c; @} s3;
+typedef struct s4 @{ s3 d; @} s4;
+
+s4 var = @{ @{ @{ @{ 3 @} @} @} @};
+@end smallexample
+
+The following table shows how different values of @var{depth} will
+effect how @code{var} is printed by @value{GDBN}:
+
+@multitable @columnfractions .3 .7
+@headitem @var{depth} setting @tab Result of @samp{p var}
+@item unlimited
+@tab @code{$1 = @{d = @{c = @{b = @{a = 3@}@}@}@}}
+@item @code{0}
+@tab @code{$1 = @{...@}}
+@item @code{1}
+@tab @code{$1 = @{d = @{...@}@}}
+@item @code{2}
+@tab @code{$1 = @{d = @{c = @{...@}@}@}}
+@item @code{3}
+@tab @code{$1 = @{d = @{c = @{b = @{...@}@}@}@}}
+@item @code{4}
+@tab @code{$1 = @{d = @{c = @{b = @{a = 3@}@}@}@}}
+@end multitable
+
+To see the contents of structures that have been hidden the user can
+either increase the print max-depth, or they can print the elements of
+the structure that are visible, for example
+
+@smallexample
+(gdb) set print max-depth 2
+(gdb) p var
+$1 = @{d = @{c = @{...@}@}@}
+(gdb) p var.d
+$2 = @{c = @{b = @{...@}@}@}
+(gdb) p var.d.c
+$3 = @{b = @{a = 3@}@}
+@end smallexample
+
+The pattern used to replace nested structures varies based on
+language, for most languages @code{@{...@}} is used, but Fortran uses
+@code{(...)}.
+
+@item show print max-depth
+Display the current threshold after which nested structures are
+replaces with ellipsis.
+
 @item set print null-stop
 @cindex @sc{null} elements in arrays
 Cause @value{GDBN} to stop printing the characters of an array when the first

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

* Re: [PATCHv2 5/5] gdb: Introduce 'print max-depth' feature
  2019-04-18 21:52           ` Andrew Burgess
@ 2019-04-19  6:50             ` Eli Zaretskii
  0 siblings, 0 replies; 33+ messages in thread
From: Eli Zaretskii @ 2019-04-19  6:50 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches, philippe.waroquiers

> Date: Thu, 18 Apr 2019 22:52:32 +0100
> From: Andrew Burgess <andrew.burgess@embecosm.com>
> Cc: gdb-patches@sourceware.org,	Philippe Waroquiers <philippe.waroquiers@skynet.be>
> 
> Eli, thanks for the feedback.
> 
> Having taken Philippe's feedback on board too, I have now adjusted how
> I show the effects of the different depth settings to use a table
> instead of a set of paragraphs.
> 
> I just wanted to run this by you to make sure I hadn't made any silly
> style mistakes.
> 
> The diff below is only for gdb/doc/gdb.texinfo - this is the only file
> I've changed over the previous version of the patch.

LGTM, thanks.

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

* Re: [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests
  2019-04-16 23:06   ` [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests Andrew Burgess
@ 2019-04-19 14:31     ` Tom Tromey
  0 siblings, 0 replies; 33+ messages in thread
From: Tom Tromey @ 2019-04-19 14:31 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> We currently add the -fno-stack-protector option to the compiler flags
Andrew> when compiling rust tests, as this is not a valid rustcc flag, this
Andrew> means non of the test tests will compile.

Andrew> gdb/testsuite/ChangeLog:

Andrew> 	* lib/gdb.exp (gdb_compile): Don't add -fno-stack-protector option
Andrew> 	if we're using rustcc.

Thanks for doing this.  This is ok.

Tom

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

* Re: [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis
  2019-04-16 23:06   ` [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
@ 2019-04-19 14:37     ` Tom Tromey
  0 siblings, 0 replies; 33+ messages in thread
From: Tom Tromey @ 2019-04-19 14:37 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> In this commit I add the new field and initialise it for each
Andrew> language, however at this stage the new field is never used.

Andrew> gdb/ChangeLog:

Andrew> 	* language.h (struct language_defn) <la_struct_too_deep_ellipsis>:
Andrew> 	New field.
Andrew> 	* ada-lang.c (ada_language_defn): Initialise new field.
Andrew> 	* c-lang.c (c_language_defn): Likewise.
Andrew> 	(cplus_language_defn): Likewise.
Andrew> 	(asm_language_defn): Likewise.
Andrew> 	(minimal_language_defn): Likewise.
Andrew> 	* d-lang.c (d_language_defn): Likewise.
Andrew> 	* f-lang.c (f_language_defn): Likewise.
Andrew> 	* go-lang.c (go_language_defn): Likewise.
Andrew> 	* language.c (unknown_language_defn): Likewise.
Andrew> 	(auto_language_defn): Likewise.
Andrew> 	* m2-lang.c (m2_language_defn): Likewise.
Andrew> 	* objc-lang.c (objc_language_defn): Likewise.
Andrew> 	* opencl-lang.c (opencl_language_defn): Likewise.
Andrew> 	* p-lang.c (pascal_language_defn): Likewise.
Andrew> 	* rust-lang.c (rust_language_defn): Likewise.

This looks good to me, thanks.

Tom

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-16 23:06   ` [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p Andrew Burgess
  2019-04-18 17:08     ` Pedro Alves
@ 2019-04-19 14:45     ` Tom Tromey
  2019-04-19 22:22       ` Andrew Burgess
  2019-04-19 22:45       ` Andrew Burgess
  1 sibling, 2 replies; 33+ messages in thread
From: Tom Tromey @ 2019-04-19 14:45 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> Some languages already have a "is this a string" predicate that I was
Andrew> able to reuse, while for other languages I've had to add a new
Andrew> predicate.  In this case I took inspiration from the value printing
Andrew> code for that language - what different conditions would result in
Andrew> printing something as a string.

This looks essentially fine, but I had some questions.

Andrew> +bool
Andrew> +c_is_string_type_p (struct type *type)
Andrew> +{
Andrew> +  type = check_typedef (type);
Andrew> +  while (TYPE_CODE (type) == TYPE_CODE_REF)
Andrew> +    {
Andrew> +      type = TYPE_TARGET_TYPE (type);
Andrew> +      type = check_typedef (type);
Andrew> +    }
Andrew> +
Andrew> +  switch (TYPE_CODE (type))
Andrew> +    {
Andrew> +    case TYPE_CODE_ARRAY:
Andrew> +      {
Andrew> +	/* See if target type looks like a string.  */
Andrew> +	struct type *array_target_type = TYPE_TARGET_TYPE (type);
Andrew> +	return (TYPE_LENGTH (type) > 0
Andrew> +		&& TYPE_LENGTH (array_target_type) > 0
Andrew> +		&& c_textual_element_type (array_target_type, 0));
Andrew> +      }
Andrew> +    case TYPE_CODE_STRING:
Andrew> +      return true;

It seems to me that a "char *" should be considered a string in C;
and probably a "wchar_t *" as well.  Maybe see c-lang.c:classify_type.

Andrew> +/* Return true if TYPE is a string type.  */
Andrew> +static bool
Andrew> +rust_is_string_type_p (struct type *type)
Andrew> +{
Andrew> +  LONGEST low_bound, high_bound;
Andrew> +
Andrew> +  type = check_typedef (type);
Andrew> +  return ((TYPE_CODE (type) == TYPE_CODE_STRING)
Andrew> +	  || (TYPE_CODE (type) == TYPE_CODE_PTR
Andrew> +	      && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_ARRAY
Andrew> +		  && rust_u8_type_p (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)))
Andrew> +		  && get_array_bounds (TYPE_TARGET_TYPE (type), &low_bound,
Andrew> +				       &high_bound)))
Andrew> +	  || ((TYPE_CODE (type) == TYPE_CODE_UNION
Andrew> +	       || (TYPE_CODE (type) == TYPE_CODE_STRUCT
Andrew> +		   && !rust_enum_p (type)))
Andrew> +	      && rust_slice_type_p (type)
Andrew> +	      && strcmp (TYPE_NAME (type), "&str") == 0));

I didn't understand the reason for TYPE_CODE_UNION here.

Also, I think an array or slice of 'char' should probably be considered
a string in Rust.  See rust_chartype_p.

thanks,
Tom

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-18 17:08     ` Pedro Alves
@ 2019-04-19 21:00       ` Andrew Burgess
  0 siblings, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-04-19 21:00 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

* Pedro Alves <palves@redhat.com> [2019-04-18 18:07:55 +0100]:

> On 4/17/19 12:06 AM, Andrew Burgess wrote:
> > This commit is preparation work for the next commit, and by itself
> > makes no user visible change to GDB.  I've split this work into a
> > separate commit in order to make code review easier.
> > 
> > This commit adds a new field 'la_is_string_type_p' to the language
> > struct, this predicate will return true if a type is a string type for
> > the given language.
> > 
> > Some languages already have a "is this a string" predicate that I was
> > able to reuse, while for other languages I've had to add a new
> > predicate.  In this case I took inspiration from the value printing
> > code for that language - what different conditions would result in
> > printing something as a string.
> > 
> > A default "is this a string" method has also been added that looks for
> > TYPE_CODE_STRING, this is the fallback I've used for a couple of
> > languages.
> > 
> > In this commit I add the new field and initialise it for each
> > language, however at this stage the new field is never used.
> > 
> 
> Thanks.  This nicely addresses one of my previous review comments.
> 
> I can't speak to whether the implementation for the different languages
> are correct.  E.g., I'm curious from where you extracted the M2 and
> the Rust bits.  Didn't seem like it was a refactoring job?

It was mostly me looking for calls to LA_PRINT_STRING in each
languages value printing code then combining all of the conditions
required to reach these calls.

Could we refactor to make use of these new is_string functions within
the value printing code?  Sure, but I don't think it would be pretty.
Most languages base their value printing around a switch statement,
and also have additional format related conditions mixed in with the
logic.

Ideally, at least for this commit I'd prefer to no mess with the value
printing code more than I need too...

Thanks,
Andrew




> 
> Formatting comments below.
> 
> > +/* Return true if TYPE is a string.  */
> > +static bool
> 
> Empty line after comment.
> 
> > +static bool
> > +m2_is_string_type_p (struct type *type)
> > +{
> > +  type = check_typedef (type);
> > +  if (TYPE_CODE (type) == TYPE_CODE_ARRAY
> > +      && TYPE_LENGTH (type) > 0
> > +      && TYPE_LENGTH (TYPE_TARGET_TYPE (type)) > 0)
> > +    {
> > +      struct type *elttype = check_typedef (TYPE_TARGET_TYPE (type));
> > +
> > +      if (TYPE_LENGTH (elttype) == 1 &&
> 
> && goes on the next line.
> 
> > +	  (TYPE_CODE (elttype) == TYPE_CODE_INT
> > +	   || TYPE_CODE (elttype) == TYPE_CODE_CHAR))
> > +	return true;
> > +    }
> > +
> > +  return false;
> > +}
> > +
> 
> Thanks,
> Pedro Alves

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-19 14:45     ` Tom Tromey
@ 2019-04-19 22:22       ` Andrew Burgess
  2019-04-24 19:32         ` Tom Tromey
  2019-04-19 22:45       ` Andrew Burgess
  1 sibling, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-19 22:22 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

* Tom Tromey <tom@tromey.com> [2019-04-19 08:45:03 -0600]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> Some languages already have a "is this a string" predicate that I was
> Andrew> able to reuse, while for other languages I've had to add a new
> Andrew> predicate.  In this case I took inspiration from the value printing
> Andrew> code for that language - what different conditions would result in
> Andrew> printing something as a string.
> 
> This looks essentially fine, but I had some questions.
> 
> Andrew> +bool
> Andrew> +c_is_string_type_p (struct type *type)
> Andrew> +{
> Andrew> +  type = check_typedef (type);
> Andrew> +  while (TYPE_CODE (type) == TYPE_CODE_REF)
> Andrew> +    {
> Andrew> +      type = TYPE_TARGET_TYPE (type);
> Andrew> +      type = check_typedef (type);
> Andrew> +    }
> Andrew> +
> Andrew> +  switch (TYPE_CODE (type))
> Andrew> +    {
> Andrew> +    case TYPE_CODE_ARRAY:
> Andrew> +      {
> Andrew> +	/* See if target type looks like a string.  */
> Andrew> +	struct type *array_target_type = TYPE_TARGET_TYPE (type);
> Andrew> +	return (TYPE_LENGTH (type) > 0
> Andrew> +		&& TYPE_LENGTH (array_target_type) > 0
> Andrew> +		&& c_textual_element_type (array_target_type, 0));
> Andrew> +      }
> Andrew> +    case TYPE_CODE_STRING:
> Andrew> +      return true;
> 
> It seems to me that a "char *" should be considered a string in C;
> and probably a "wchar_t *" as well.  Maybe see c-lang.c:classify_type.

Thanks, I'll take a look at these cases.

> 
> Andrew> +/* Return true if TYPE is a string type.  */
> Andrew> +static bool
> Andrew> +rust_is_string_type_p (struct type *type)
> Andrew> +{
> Andrew> +  LONGEST low_bound, high_bound;
> Andrew> +
> Andrew> +  type = check_typedef (type);
> Andrew> +  return ((TYPE_CODE (type) == TYPE_CODE_STRING)
> Andrew> +	  || (TYPE_CODE (type) == TYPE_CODE_PTR
> Andrew> +	      && (TYPE_CODE (TYPE_TARGET_TYPE (type)) == TYPE_CODE_ARRAY
> Andrew> +		  && rust_u8_type_p (TYPE_TARGET_TYPE (TYPE_TARGET_TYPE (type)))
> Andrew> +		  && get_array_bounds (TYPE_TARGET_TYPE (type), &low_bound,
> Andrew> +				       &high_bound)))
> Andrew> +	  || ((TYPE_CODE (type) == TYPE_CODE_UNION
> Andrew> +	       || (TYPE_CODE (type) == TYPE_CODE_STRUCT
> Andrew> +		   && !rust_enum_p (type)))
> Andrew> +	      && rust_slice_type_p (type)
> Andrew> +	      && strcmp (TYPE_NAME (type), "&str") == 0));
> 
> I didn't understand the reason for TYPE_CODE_UNION here.

Most of the _is_string functions were created by looking through the
language's value printing code looking for places where the language
identifies something as a string, then taking all of the conditions
that led to that point and placing them in the _is_string function.

For rust, rust_val_print_str is called from val_print_struct, which is
called for TYPE_CODE_STRUCT and TYPE_CODE_UNION, hence the above...

...but, after prompting, I took a closer look, and rust_slice_type_p
is only true for TYPE_CODE_STRUCT, which makes the union check
redundant - so its gone!

> 
> Also, I think an array or slice of 'char' should probably be considered
> a string in Rust.  See rust_chartype_p.

That sounds sensible, but .... I don't believe these things that you
describe are currently printed as strings.  Here's what I tried
(excuse my poor rust skills, maybe I'm not trying what you're
suggesting):

    $ cat str.rs
    #![allow(dead_code)]
    #![allow(unused_variables)]
    #![allow(unused_assignments)]
    
    fn main () {
       let str1: &str = "str1";
       let str2: String = "str2".to_string();
       let str3: [char; 4] = [ 's', 't', 'r', '3' ];
       let str4: &[char] = &str3;
    
       println!("hello world");	// Break here.
    }
    $ rustc -g -o str str.rs 
    $ gdb str
    <... snip ...>
    (gdb) b 11
    Breakpoint 1 at 0x3d3b: file str.rs, line 11.
    (gdb) r
    <... snip ...>
    Breakpoint 1, str::main () at str.rs:11
    11	   println!("hello world");	// Break here.
    (gdb) p str1
    $1 = "str1"
    (gdb) p str2
    $2 = alloc::string::String {vec: alloc::vec::Vec<u8> {buf: alloc::raw_vec::RawVec<u8, alloc::alloc::Global> {ptr: core::ptr::Unique<u8> {pointer: core::nonzero::NonZero<*const u8> (0x555555784a40 "str2\000"), _marker: core::marker::PhantomData<u8>}, cap: 4, a: alloc::alloc::Global}, len: 4}}
    (gdb) p str3
    $3 = [115 's', 116 't', 114 'r', 51 '3']
    (gdb) p str4
    $4 = &[char] {data_ptr: 0x7fffffffcd90 "s\000\000\000t\000\000\000r\000\000\000\063\000\000\000\xffffcd90\x7fff\004\000\000\000", length: 4}

So I think that currently only the '&str' (String slice?) is printed
as a string.

Like I said, my rust-foo is weak, so if you have an example of a
different type that is currently printed as a string then I'm happy to
fold this info a rust test and make any fixes for rust's _is_string
function as needed.

You'll have noticed (maybe?) that the original patch didn't include a
rust test at all.  This was because some of rusts value printing seems
a little broken right now.  For example, printing an array slice
(&str) variable works fine, but place this inside a struct and it no
longer works, for example:

  $ cat nested.rs
  #![allow(dead_code)]
  #![allow(unused_variables)]
  #![allow(unused_assignments)]
  
  pub struct Bad {
      pub field1: i32,
      pub field2: &'static str,
  }
  
  fn main () {
     let s = Bad { field1: 1, field2: "hello" };
     println!("hello world");	// Break here.
  }
  $ rustc -g -o nested nested.rs 
  $ gdb nested
  <... snip ...>
  (gdb) b 12
  Breakpoint 1 at 0x3d00: file nested.rs, line 12.
  (gdb) r
  <... snip ...>
  Breakpoint 1, nested::main () at nested.rs:12
  12	   println!("hello world");	// Break here.
  (gdb) p s
  $1 = nested::Bad {field1: 1, field2: <error reading variable>}
  (gdb) 

However, I fixed that with this patch:

    diff --git a/gdb/rust-lang.c b/gdb/rust-lang.c
    index 2fada465d65..597986a4b34 100644
    --- a/gdb/rust-lang.c
    +++ b/gdb/rust-lang.c
    @@ -378,6 +378,8 @@ val_print_struct (struct type *type, int embedded_offset,

       if (rust_slice_type_p (type) && strcmp (TYPE_NAME (type), "&str") == 0)
         {
    +      if (embedded_offset != 0)
    +	val = value_at_lazy (type, value_address (val) + embedded_offset);
           rust_val_print_str (stream, val, options);
           return;
         }

So, I think the summary is, I'm happy to fix rust_is_string_p to cover
any cases that currently print as a string, but I think that if
something _doesn't_ currently print as a string then rust_is_string_p
shouldn't identify it as a string - if it did then we'd end up
printing a structure at a depth when it should have been replaced with
ellipsis.

Thanks,
Andrew

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-19 14:45     ` Tom Tromey
  2019-04-19 22:22       ` Andrew Burgess
@ 2019-04-19 22:45       ` Andrew Burgess
  2019-04-24 19:33         ` Tom Tromey
  1 sibling, 1 reply; 33+ messages in thread
From: Andrew Burgess @ 2019-04-19 22:45 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

* Tom Tromey <tom@tromey.com> [2019-04-19 08:45:03 -0600]:

> >>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:
> 
> Andrew> Some languages already have a "is this a string" predicate that I was
> Andrew> able to reuse, while for other languages I've had to add a new
> Andrew> predicate.  In this case I took inspiration from the value printing
> Andrew> code for that language - what different conditions would result in
> Andrew> printing something as a string.
> 
> This looks essentially fine, but I had some questions.
> 
> Andrew> +bool
> Andrew> +c_is_string_type_p (struct type *type)
> Andrew> +{
> Andrew> +  type = check_typedef (type);
> Andrew> +  while (TYPE_CODE (type) == TYPE_CODE_REF)
> Andrew> +    {
> Andrew> +      type = TYPE_TARGET_TYPE (type);
> Andrew> +      type = check_typedef (type);
> Andrew> +    }
> Andrew> +
> Andrew> +  switch (TYPE_CODE (type))
> Andrew> +    {
> Andrew> +    case TYPE_CODE_ARRAY:
> Andrew> +      {
> Andrew> +	/* See if target type looks like a string.  */
> Andrew> +	struct type *array_target_type = TYPE_TARGET_TYPE (type);
> Andrew> +	return (TYPE_LENGTH (type) > 0
> Andrew> +		&& TYPE_LENGTH (array_target_type) > 0
> Andrew> +		&& c_textual_element_type (array_target_type, 0));
> Andrew> +      }
> Andrew> +    case TYPE_CODE_STRING:
> Andrew> +      return true;
> 
> It seems to me that a "char *" should be considered a string in C;
> and probably a "wchar_t *" as well.  Maybe see c-lang.c:classify_type.

Do you think that this applied on top of the previous would catch all
of the pointer cases?

I don't think we need to call classify_type as that seems to assume
that by the time it's called we know we have a character type of some
description, so I think calling c_textual_element_type is enough...

Let me know if you think I've missed something,

thanks,
Andrew

---

diff --git a/gdb/c-lang.c b/gdb/c-lang.c
index 4d5284e2e80..aeffefad55e 100644
--- a/gdb/c-lang.c
+++ b/gdb/c-lang.c
@@ -739,6 +739,11 @@ c_is_string_type_p (struct type *type)
       }
     case TYPE_CODE_STRING:
       return true;
+    case TYPE_CODE_PTR:
+      {
+	struct type *element_type = TYPE_TARGET_TYPE (type);
+	return c_textual_element_type (element_type, 0);
+      }
     default:
       break;
     }

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-19 22:22       ` Andrew Burgess
@ 2019-04-24 19:32         ` Tom Tromey
  0 siblings, 0 replies; 33+ messages in thread
From: Tom Tromey @ 2019-04-24 19:32 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom Tromey, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

Andrew> ...but, after prompting, I took a closer look, and rust_slice_type_p
Andrew> is only true for TYPE_CODE_STRUCT, which makes the union check
Andrew> redundant - so its gone!

Thanks.

>> Also, I think an array or slice of 'char' should probably be considered
>> a string in Rust.  See rust_chartype_p.

Andrew> That sounds sensible, but .... I don't believe these things that you
Andrew> describe are currently printed as strings.

Thanks for looking into that.  Maybe it's just an oversight in the Rust
code, or maybe it's just uncommon to do this kind of thing.  Not your
problem :)

Andrew> You'll have noticed (maybe?) that the original patch didn't include a
Andrew> rust test at all.  This was because some of rusts value printing seems
Andrew> a little broken right now.  For example, printing an array slice
Andrew> (&str) variable works fine, but place this inside a struct and it no
Andrew> longer works, for example:

Thanks for noticing that.  I can deal with it, unless you want to.

Andrew> So, I think the summary is, I'm happy to fix rust_is_string_p to cover
Andrew> any cases that currently print as a string, but I think that if
Andrew> something _doesn't_ currently print as a string then rust_is_string_p
Andrew> shouldn't identify it as a string - if it did then we'd end up
Andrew> printing a structure at a depth when it should have been replaced with
Andrew> ellipsis.

Agreed.

Tom

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

* Re: [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p
  2019-04-19 22:45       ` Andrew Burgess
@ 2019-04-24 19:33         ` Tom Tromey
  0 siblings, 0 replies; 33+ messages in thread
From: Tom Tromey @ 2019-04-24 19:33 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Tom Tromey, gdb-patches

>>>>> "Andrew" == Andrew Burgess <andrew.burgess@embecosm.com> writes:

>> It seems to me that a "char *" should be considered a string in C;
>> and probably a "wchar_t *" as well.  Maybe see c-lang.c:classify_type.

Andrew> Do you think that this applied on top of the previous would catch all
Andrew> of the pointer cases?

I think that should be fine, thanks.

Tom

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

* Re: [PATCHv2 0/5] Add new 'print max-depth' feature
  2019-04-16 23:06 [PATCHv2 0/5] Add new 'print max-depth' feature Andrew Burgess
  2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
@ 2019-04-29 21:14 ` Andrew Burgess
  1 sibling, 0 replies; 33+ messages in thread
From: Andrew Burgess @ 2019-04-29 21:14 UTC (permalink / raw)
  To: gdb-patches

Thank you for all the feedback.

I've now pushed this series.

Thanks,
Andrew


* Andrew Burgess <andrew.burgess@embecosm.com> [2019-04-17 00:06:05 +0100]:

> This revision hopefully addresses all of the feedback from the first
> version.
> 
> Patches #1 and #2 are new, minor clean up patches.
> 
> Patch #3 (previously #1) is unchanged.
> 
> Patch #4 is new, this adds a new predicate to the language structure
> to decide if a given type is a string or not.
> 
> Patch #5 this is updated from the previous version, there's more
> documentation, many more tests, minor code issues identified in v1
> have been addressed.
> 
> --
> 
> Andrew Burgess (5):
>   gdb/ada: Update some predicate functions to return bool
>   gdb/testsuite: Don't add gcc flags when compiling rust tests
>   gdb: Introduce new language field la_struct_too_deep_ellipsis
>   gdb: Introduce new language field la_is_string_type_p
>   gdb: Introduce 'print max-depth' feature
> 
>  gdb/ChangeLog                                 |  85 +++++++++
>  gdb/NEWS                                      |   9 +
>  gdb/ada-lang.c                                |  12 +-
>  gdb/ada-lang.h                                |   4 +-
>  gdb/c-lang.c                                  |  47 ++++-
>  gdb/c-lang.h                                  |   5 +
>  gdb/cp-valprint.c                             |  80 +++++----
>  gdb/d-lang.c                                  |   4 +-
>  gdb/doc/ChangeLog                             |   8 +
>  gdb/doc/gdb.texinfo                           |  70 ++++++++
>  gdb/doc/guile.texi                            |   3 +
>  gdb/doc/python.texi                           |   8 +
>  gdb/f-lang.c                                  |  14 +-
>  gdb/go-lang.c                                 |  14 +-
>  gdb/guile/scm-pretty-print.c                  |  19 +-
>  gdb/language.c                                |  22 ++-
>  gdb/language.h                                |  13 ++
>  gdb/m2-lang.c                                 |  25 ++-
>  gdb/objc-lang.c                               |   4 +-
>  gdb/opencl-lang.c                             |   4 +-
>  gdb/p-lang.c                                  |  14 +-
>  gdb/python/py-prettyprint.c                   |  15 +-
>  gdb/python/py-value.c                         |   7 +-
>  gdb/rust-lang.c                               |  24 ++-
>  gdb/testsuite/ChangeLog                       |  23 +++
>  gdb/testsuite/gdb.base/max-depth.c            | 232 +++++++++++++++++++++++++
>  gdb/testsuite/gdb.base/max-depth.exp          | 165 ++++++++++++++++++
>  gdb/testsuite/gdb.fortran/max-depth.exp       |  41 +++++
>  gdb/testsuite/gdb.fortran/max-depth.f90       |  42 +++++
>  gdb/testsuite/gdb.go/max-depth.exp            |  40 +++++
>  gdb/testsuite/gdb.go/max-depth.go             |  41 +++++
>  gdb/testsuite/gdb.modula2/max-depth.c         |  45 +++++
>  gdb/testsuite/gdb.modula2/max-depth.exp       |  37 ++++
>  gdb/testsuite/gdb.python/py-format-string.exp |  21 +++
>  gdb/testsuite/gdb.python/py-nested-maps.c     | 130 ++++++++++++++
>  gdb/testsuite/gdb.python/py-nested-maps.exp   | 238 ++++++++++++++++++++++++++
>  gdb/testsuite/gdb.python/py-nested-maps.py    | 135 +++++++++++++++
>  gdb/testsuite/lib/gdb.exp                     |  36 +++-
>  gdb/valprint.c                                |  56 +++++-
>  gdb/valprint.h                                |  12 ++
>  40 files changed, 1746 insertions(+), 58 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.base/max-depth.c
>  create mode 100644 gdb/testsuite/gdb.base/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.fortran/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.fortran/max-depth.f90
>  create mode 100644 gdb/testsuite/gdb.go/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.go/max-depth.go
>  create mode 100644 gdb/testsuite/gdb.modula2/max-depth.c
>  create mode 100644 gdb/testsuite/gdb.modula2/max-depth.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.c
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.exp
>  create mode 100644 gdb/testsuite/gdb.python/py-nested-maps.py
> 
> -- 
> 2.14.5
> 

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

end of thread, other threads:[~2019-04-29 21:14 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-16 23:06 [PATCHv2 0/5] Add new 'print max-depth' feature Andrew Burgess
2019-03-27 21:53 ` [PATCH 0/2] " Andrew Burgess
2019-03-27 21:53   ` [PATCH 1/2] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
2019-03-28 12:43     ` Pedro Alves
2019-03-27 21:53   ` [PATCH 2/2] gdb: Introduce 'print max-depth' feature Andrew Burgess
2019-03-28 12:42     ` Pedro Alves
2019-03-28 16:08     ` Eli Zaretskii
2019-03-28 12:47   ` [PATCH 0/2] Add new " Pedro Alves
2019-03-28 22:48     ` Andrew Burgess
2019-03-28 23:47       ` Marco Barisione
2019-04-16 23:06   ` [PATCHv2 5/5] gdb: Introduce " Andrew Burgess
2019-04-17  7:42     ` Philippe Waroquiers
2019-04-17 14:35     ` Eli Zaretskii
2019-04-18  1:07       ` Andrew Burgess
2019-04-18  2:38         ` Eli Zaretskii
2019-04-18 21:52           ` Andrew Burgess
2019-04-19  6:50             ` Eli Zaretskii
2019-04-18 17:08     ` Pedro Alves
2019-04-16 23:06   ` [PATCHv2 2/5] gdb/testsuite: Don't add gcc flags when compiling rust tests Andrew Burgess
2019-04-19 14:31     ` Tom Tromey
2019-04-16 23:06   ` [PATCHv2 1/5] gdb/ada: Update some predicate functions to return bool Andrew Burgess
2019-04-18  0:03     ` Joel Brobecker
2019-04-16 23:06   ` [PATCHv2 3/5] gdb: Introduce new language field la_struct_too_deep_ellipsis Andrew Burgess
2019-04-19 14:37     ` Tom Tromey
2019-04-16 23:06   ` [PATCHv2 4/5] gdb: Introduce new language field la_is_string_type_p Andrew Burgess
2019-04-18 17:08     ` Pedro Alves
2019-04-19 21:00       ` Andrew Burgess
2019-04-19 14:45     ` Tom Tromey
2019-04-19 22:22       ` Andrew Burgess
2019-04-24 19:32         ` Tom Tromey
2019-04-19 22:45       ` Andrew Burgess
2019-04-24 19:33         ` Tom Tromey
2019-04-29 21:14 ` [PATCHv2 0/5] Add new 'print max-depth' feature Andrew Burgess

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).