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"
+	}
+    }
+}
+
 
 
 # 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);
+}
 
 
 /* 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
