diff --git a/gdb/contrib/gather-valgrind-results.sh b/gdb/contrib/gather-valgrind-results.sh
new file mode 100755
index 00000000000..9cbce030b16
--- /dev/null
+++ b/gdb/contrib/gather-valgrind-results.sh
@@ -0,0 +1,215 @@
+#! /bin/bash
+
+# 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 script should be used by the testing framework once the tests
+# have been run under valgrind.  This script looks for all of the
+# valgrind.out files that have been generated and gathers the error
+# summaries into a single file, one line per test script, like this:
+#
+#   gdb.xxx/example.exp: 10 errors from 5 contexts
+#
+# Note that a single test script might invoke GDB multiple times,
+# resulting in multiple valgrind.out files for that test script.  In
+# this case script will merge the results into a single line.  So the
+# output directory for the above might have looked like this:
+#
+#   gdb/testsuite/outputs/gdb.xxx/example/valgrind.out
+#   gdb/testsuite/outputs/gdb.xxx/example/valgrind.out.1
+#   gdb/testsuite/outputs/gdb.xxx/example/valgrind.out.2
+#
+# The error summaries from all three of these files have been merged
+# to give the total of 10 errors from 5 contexts.
+
+# ------------------------------------------------------------------#
+
+# This script should be passed the path to the outputs directory.
+
+outputs_dir=$1
+
+# Takes a single string argument.  Format the string as an error and
+# exit.
+function error () {
+    echo "ERROR: $1"
+    exit 1
+}
+
+# Takes the absolute path to a 'gdb.cmd' file as produced by the GDB
+# testsuite (including files with a version number, for example
+# 'gdb.cmd.1') and prints a string that is the name of a script that
+# was probably run to result in that command file.
+#
+# The script name (for example 'gdb.blah/example.exp') has to be
+# guessed from the path to the command file as it is not recorded
+# anywhere.
+function cmd_file_to_testname () {
+    local filename=$(dirname "$1")
+    local p1=$(basename "$filename")
+    filename=$(dirname "$filename")
+    local p2=$(basename "$filename")
+    echo "$p2/$p1.exp"
+}
+
+# Takes the absolute path to a 'gdb.cmd' file as produced by the GDB
+# testsuite (including files with a version number, for example
+# 'gdb.cmd.1') and prints a string that is the absolute path for where
+# we expect to find the corresponding 'valgrind.out' file.
+function cmd_file_to_valgrind_file () {
+    local filename=$1
+    filename=$(echo $filename | sed -e 's/gdb.cmd/valgrind.out/')
+    echo $filename
+}
+
+# Takes the absolute path to a 'valgrind.out' file and produces a
+# short summary of the results for that file.  The summary could be
+# something like:
+#
+#    gdb.blah/example.exp:BAD
+#
+# If the valgrind output file is missing or corrupted.  If however the
+# valgrind output file is as expected we'll get something like:
+#
+#    gdb.blah/example.exp:4;2
+#
+# The first number is the number of errors, and the second is the
+# number of contexts from which the errors came.  This is extracted
+# from valgrinds "ERROR SUMMARY" line in its output file.
+function extract_valgrind_results () {
+    local gdb_cmd_file=$1
+
+    local valgrind_file=$(cmd_file_to_valgrind_file "${gdb_cmd_file}")
+    if [ ! "${valgrind_file}" -ot "${gdb_cmd_file}" ]
+    then
+        line=$(grep -m 1 "ERROR SUMMARY: " "${valgrind_file}")
+        if [ -z "${line}" ]
+        then
+            echo "$testname:BAD"
+        else
+            line=$(echo $line \ | \
+                       sed -e 's/.*ERROR SUMMARY: \([0-9]\+\) errors from \([0-9]\+\) contexts .*/\1;\2/')
+            echo "$testname:$line"
+        fi
+    else
+        echo "$testname:BAD"
+    fi
+}
+
+# This takes an absolute path to the outputs directory in the build
+# tree, which is located somewhere like:
+#
+#    /projects/binutils-gdb/build/gdb/testsuite/outputs/
+#
+# And produces a list of summary results, one per valgrind output file
+# found below the outputs directory.
+function extract_raw_errors () {
+    local output_dir=$1
+
+    while IFS= read -r cmd_file
+    do
+        gdb_cmd=$(cut -d' ' -f1 ${cmd_file})
+
+        re=\\bvalgrind\$
+        if [[ "${gdb_cmd}" =~ $re ]]
+        then
+            # This GDB was run under valgrind, we expect to find a
+            # valgrind.out file.  Failure to find one indicates
+            # something went wrong.
+            #
+            # But first, lets figure out the name for this test.
+            testname=$(cmd_file_to_testname "${cmd_file}")
+
+            # Now given the name of the GDB command file, find the
+            # corresponding valgrind output file and extract the
+            # results from it, and print them on one line.
+            extract_valgrind_results "${cmd_file}"
+        fi
+    done < <(find "${outputs_dir}" -name "gdb.cmd*")
+}
+
+# Some global state.
+last_name=""
+errors=0
+contexts=0
+is_bad=0
+
+# Takes a string that is the value part of a test summary line, this
+# will either be the string "BAD" (without quotes), or two numbers
+# like "4;2" (again without quotes).
+#
+# If the value is BAD, then the global state is updated to reflect
+# this, while if the value is two numbers then these values are added
+# into the global state.
+function add_result () {
+    local value=$1
+
+    # The following updates the global state.
+    if [ $value == "BAD" ]
+    then
+        errors=0
+        contexts=0
+        is_bad=1
+    elif [ $is_bad == 0 ]
+    then
+        e=$(echo $value | cut -d';' -f1)
+        c=$(echo $value | cut -d';' -f2)
+        errors=$((errors + e))
+        contexts=$((contexts + c))
+    fi
+}
+
+# Print a line representing the current global result state.
+function print_result () {
+    if [ $is_bad == 1 ]
+    then
+        echo "${last_name}: Valgrind output missing or corrupted"
+    else
+        echo "${last_name}: ${errors} errors from $contexts contexts"
+    fi
+}
+
+# Check we have a sane outputs directory.
+[[ -n "${outputs_dir}" && -d "${outputs_dir}" ]] \
+    || error "invalid outputs directory '${outputs_dir}'"
+
+# Loop over all sorted summary lines and merge together all results
+# for the same test file, then print the results.  Don't forget to
+# print the last result after the loop.
+while IFS= read -r result
+do
+    name=$(echo $result | cut -d: -f1)
+    value=$(echo $result | cut -d: -f2)
+
+    if [ "$name" == "$last_name" ]
+    then
+        # Add this result to the last one.
+        add_result $value
+    else
+        if [ -n "$last_name" ]
+        then
+            print_result
+        fi
+
+        last_name=$name
+        errors=0
+        contexts=0
+        is_bad=0
+
+        add_result $value
+    fi
+done < <(extract_raw_errors "$outputs_dir" | sort)
+if [ -n "$last_name" ]
+then
+    print_result
+fi
diff --git a/gdb/testsuite/Makefile.in b/gdb/testsuite/Makefile.in
index 2beba053ee6..86aaf0234d3 100644
--- a/gdb/testsuite/Makefile.in
+++ b/gdb/testsuite/Makefile.in
@@ -168,6 +168,18 @@ TIMESTAMP = $(if $(TS),| $(srcdir)/print-ts.py $(if $(TS_FORMAT),$(TS_FORMAT),),
 gdb_debug = $(if $(GDB_DEBUG),GDB_DEBUG=$(GDB_DEBUG) ; export GDB_DEBUG ;,)
 gdbserver_debug = $(if $(GDBSERVER_DEBUG),GDBSERVER_DEBUG=$(GDBSERVER_DEBUG) ; export GDBSERVER_DEBUG ;,)
 
+# Setup flags needed to activate the valgrind testing in the testsuite.
+ifeq ($(VALGRIND),1)
+  valgrind_var = VALGRIND=1 ; export VALGRIND ;
+  valgrind_command_var = $(if $(VALGRIND_COMMAND),VALGRIND_COMMAND=$(VALGRIND_COMMAND) ; export VALGRIND_COMMAND ;,)
+  valgrind_extra_args_var = $(if $(VALGRIND_EXTRA_ARGS),VALGRIND_EXTRA_ARGS=$(VALGRIND_EXTRA_ARGS) ; export VALGRIND_EXTRA_ARGS ;,)
+  valgrind_build_sum = $(srcdir)/../contrib/gather-valgrind-results.sh $(PWD)/outputs/ > valgrind.sum
+else
+  valgrind_var =
+  valgrind_command_var =
+  valgrind_extra_args_var =
+  valgrind_build_sum = true
+endif
 
 # All the hair to invoke dejagnu.  A given invocation can just append
 # $(RUNTESTFLAGS)
@@ -176,6 +188,7 @@ DO_RUNTEST = \
 	srcdir=${srcdir} ; export srcdir ; \
 	EXPECT=${EXPECT} ; export EXPECT ; \
 	EXEEXT=${EXEEXT} ; export EXEEXT ;  $(gdb_debug) $(gdbserver_debug) \
+	$(valgrind_var) $(valgrind_command_var) $(valgrind_extra_args_var) \
         $(RPATH_ENVVAR)=$$rootme/../../expect:$$rootme/../../libstdc++:$$rootme/../../tk/unix:$$rootme/../../tcl/unix:$$rootme/../../bfd:$$rootme/../../opcodes:$$$(RPATH_ENVVAR); \
 	export $(RPATH_ENVVAR); \
 	if [ -f $${rootme}/../../expect/expect ] ; then  \
@@ -203,7 +216,10 @@ check-gdb.%:
 	$(MAKE) check TESTS="gdb.$*/*.exp"
 
 check-single:
-	$(DO_RUNTEST) $(RUNTESTFLAGS) $(expanded_tests_or_none) $(TIMESTAMP)
+	$(DO_RUNTEST) $(RUNTESTFLAGS) $(expanded_tests_or_none) $(TIMESTAMP) ; \
+	result=$$?; \
+	$(valgrind_build_sum); \
+	exit $$result
 
 check-single-racy:
 	-rm -rf cache racy_outputs temp
@@ -221,6 +237,7 @@ check-single-racy:
 	  $(DO_RUNTEST) --outdir=racy_outputs/$$n $(RUNTESTFLAGS) \
 	    $(expanded_tests_or_none) $(TIMESTAMP); \
 	done; \
+	$(valgrind_build_sum); \
 	$(srcdir)/analyze-racy-logs.py \
 	  `ls racy_outputs/*/gdb.sum` > racy.sum; \
 	sed -n '/=== gdb Summary ===/,$$ p' racy.sum
@@ -229,6 +246,7 @@ check-parallel:
 	-rm -rf cache outputs temp
 	$(MAKE) -k do-check-parallel; \
 	result=$$?; \
+	$(valgrind_build_sum); \
 	$(SHELL) $(srcdir)/../../contrib/dg-extract-results.sh \
 	  `find outputs -name gdb.sum -print` > gdb.sum; \
 	$(SHELL) $(srcdir)/../../contrib/dg-extract-results.sh -L \
@@ -257,6 +275,7 @@ check-parallel-racy:
 	    racy_outputs/$$n/gdb.log; \
 	  sed -n '/=== gdb Summary ===/,$$ p' racy_outputs/$$n/gdb.sum; \
 	done; \
+	$(valgrind_build_sum); \
 	$(srcdir)/analyze-racy-logs.py \
 	  `ls racy_outputs/*/gdb.sum` > racy.sum; \
 	sed -n '/=== gdb Summary ===/,$$ p' racy.sum
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index 4795df1f759..03d43f936f8 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -139,6 +139,59 @@ tests" respectively.  "both" is the default.  GDB_PERFTEST_TIMEOUT
 specify the timeout, which is 3000 in default.  The result of
 performance test is appended in `testsuite/perftest.log'.
 
+Running Tests Under Valgrind
+****************************
+
+GDB Testsuite includes features to enable easy testing under the
+valgrind memory checking tool.  The simplest way to get started is to
+pass VALGRIND=1 as part of the make check command line, like this:
+
+	make check VALGRIND=1
+
+This will run GDB under valgrind.  Each invokation of GDB will create
+a new output file in the testsuite outputs directory, the first output
+file will be valgrind.out, then valgrind.out.1, valgrind.out.2, etc.
+The corresponding gdb.cmd file will be updated to include the valgrind
+command line details (which includes the GDB command run under
+valgrind).
+
+By default GDB just runs 'valgrind' and relies on finding a suitable
+binary in your $PATH.  You can override this behaviour by passing the
+VALGRIND_COMMAND flag make, like this:
+
+	make check VALGRIND=1 VALGRIND_COMMAND=/path/to/valgrind
+
+It is possible to add extra command line flags to valgrind like this:
+
+	make check VALGRIND=1 VALGRIND_EXTRA_ARGS="--leak-check=full"
+
+The default command line arguments used for valgrind are:
+
+	valgrind --tool=memcheck \
+                 --log-file=/path/to/log \
+                 --suppressions=/path/to/file
+
+Any extra flags provided by the user are appended to the valgrind
+command line.
+
+Notice that by default a suppressions file is passed to valgrind, this
+is because there are a large number of errors from the guile garbage
+collector that make the results very noisy, the suppressions file
+hides these errors.  The default suppressions file can be found in the
+testsuite source directory here:
+
+	binutils-gdb/gdb/testsuite/valgrind-gdb.supp
+
+Finally, once a test run has completed a summary of the valgrind
+results will be created into a file:
+
+	build/gdb/testsuite/valgrind.sum
+
+This summary file lists for each test script the number of valgrind
+errors found.  This would be useful for comparing between two
+different test runs and quickly spotting if any extra errors have
+appeared.
+
 Testsuite Parameters
 ********************
 
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index acbeb013768..008862ede24 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -1695,9 +1695,39 @@ proc default_gdb_spawn { } {
     global GDB
     global INTERNAL_GDBFLAGS GDBFLAGS
     global gdb_spawn_id
+    global srcdir
 
     gdb_stop_suppressing_tests
 
+    set gdb_cmd $GDB
+    if { [info exists ::env(VALGRIND)] == 1 && $::env(VALGRIND) == 1 } {
+	set log_file \
+	    [standard_output_file_with_gdb_instance valgrind.out]
+	set sfile "$srcdir/valgrind-gdb.supp"
+
+	set valgrind_args "--tool=memcheck --log-file=${log_file}"
+	if [file exists "${sfile}"] {
+	    set valgrind_args "$valgrind_args --suppressions=${sfile}"
+	}
+	if { [info exists ::env(VALGRIND_EXTRA_ARGS)] == 1 } {
+	    set valgrind_args "$valgrind_args $::env(VALGRIND_EXTRA_ARGS)"
+	}
+
+	set valgrind_exe "valgrind"
+	if { [info exists ::env(VALGRIND_COMMAND)] == 1 } {
+	    set valgrind_exe $::env(VALGRIND_COMMAND)
+	}
+
+	if ![is_remote host] {
+	    if { [which $valgrind_exe] == 0 } then {
+		perror "$valgrind_exe does not exist."
+		exit 1
+	    }
+	}
+
+	set gdb_cmd "${valgrind_exe} ${valgrind_args} ${GDB}"
+    }
+
     # Set the default value, it may be overriden later by specific testfile.
     #
     # Use `set_board_info use_gdb_stub' for the board file to flag the inferior
@@ -1707,8 +1737,8 @@ proc default_gdb_spawn { } {
     # a specific different target protocol itself.
     set use_gdb_stub [target_info exists use_gdb_stub]
 
-    verbose "Spawning $GDB $INTERNAL_GDBFLAGS $GDBFLAGS"
-    gdb_write_cmd_file "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS"
+    verbose "Spawning $gdb_cmd $INTERNAL_GDBFLAGS $GDBFLAGS"
+    gdb_write_cmd_file "$gdb_cmd $INTERNAL_GDBFLAGS $GDBFLAGS"
 
     if [info exists gdb_spawn_id] {
 	return 0
@@ -1720,9 +1750,9 @@ proc default_gdb_spawn { } {
 	    exit 1
 	}
     }
-    set res [remote_spawn host "$GDB $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts]"]
+    set res [remote_spawn host "$gdb_cmd $INTERNAL_GDBFLAGS $GDBFLAGS [host_info gdb_opts]"]
     if { $res < 0 || $res == "" } {
-	perror "Spawning $GDB failed."
+	perror "Spawning $gdb_cmd failed."
 	return 1
     }
 
diff --git a/gdb/testsuite/valgrind-gdb.supp b/gdb/testsuite/valgrind-gdb.supp
new file mode 100644
index 00000000000..f2a25eb0117
--- /dev/null
+++ b/gdb/testsuite/valgrind-gdb.supp
@@ -0,0 +1,13 @@
+{
+  <guile_garbage_collection_1>
+  Memcheck:Cond
+  ...
+  obj:*libgc*
+}
+
+{
+  <guile_garbage_collection_2>
+  Memcheck:Value8
+  ...
+  obj:*libgc*
+}
