aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.python
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/testsuite/gdb.python')
-rw-r--r--gdb/testsuite/gdb.python/py-read-memory-leak.c27
-rw-r--r--gdb/testsuite/gdb.python/py-read-memory-leak.exp44
-rw-r--r--gdb/testsuite/gdb.python/py-read-memory-leak.py92
3 files changed, 163 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.c b/gdb/testsuite/gdb.python/py-read-memory-leak.c
new file mode 100644
index 0000000..75035cd
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-read-memory-leak.c
@@ -0,0 +1,27 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2014-2024 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/>. */
+
+static struct x
+{
+ char unsigned u[4096];
+} x, *px = &x;
+
+int
+main (void)
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.exp b/gdb/testsuite/gdb.python/py-read-memory-leak.exp
new file mode 100644
index 0000000..52b072f
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-read-memory-leak.exp
@@ -0,0 +1,44 @@
+# Copyright (C) 2024 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 checks for memory leaks
+# associated with calling gdb.Inferior.read_memory().
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Skip this test if the tracemalloc module is not available.
+if { ![gdb_py_module_available "tracemalloc"] } {
+ unsupported "tracemalloc module not available"
+ return
+}
+
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+# Source the Python script, this runs the test (which is written
+# completely in Python), and either prints PASS, or throws an
+# exception.
+gdb_test "source ${pyfile}" "PASS" "source python script"
diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.py b/gdb/testsuite/gdb.python/py-read-memory-leak.py
new file mode 100644
index 0000000..62369b3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-read-memory-leak.py
@@ -0,0 +1,92 @@
+# Copyright (C) 2024 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/>.
+
+import os
+import tracemalloc
+import gdb
+
+# A global variable in which we store a reference to the memory buffer
+# returned from gdb.Inferior.read_memory().
+mem_buf = None
+
+
+# A global filters list, we only care about memory allocations
+# originating from this script.
+filters = [tracemalloc.Filter(True, "*" + os.path.basename(__file__))]
+
+
+# Run the test. When CLEAR is True we clear the global INF variable
+# before comparing the before and after memory allocation traces.
+# When CLEAR is False we leave INF set to reference the gdb.Inferior
+# object, thus preventing the gdb.Inferior from being deallocated.
+def test(clear):
+ global filters, mem_buf
+
+ addr = gdb.parse_and_eval("px")
+ inf = gdb.inferiors()[0]
+
+ # Start tracing, and take a snapshot of the current allocations.
+ tracemalloc.start()
+ snapshot1 = tracemalloc.take_snapshot()
+
+ # Read from the inferior, this allocate a memory buffer object.
+ mem_buf = inf.read_memory(addr, 4096)
+
+ # Possibly clear the global INF variable.
+ if clear:
+ mem_buf = None
+
+ # Now grab a second snapshot of memory allocations, and stop
+ # tracing memory allocations.
+ snapshot2 = tracemalloc.take_snapshot()
+ tracemalloc.stop()
+
+ # Filter the snapshots; we only care about allocations originating
+ # from this file.
+ snapshot1 = snapshot1.filter_traces(filters)
+ snapshot2 = snapshot2.filter_traces(filters)
+
+ # Compare the snapshots, this leaves only things that were
+ # allocated, but not deallocated since the first snapshot.
+ stats = snapshot2.compare_to(snapshot1, "traceback")
+
+ # Total up all the allocated things.
+ total = 0
+ for stat in stats:
+ total += stat.size_diff
+ return total
+
+
+# The first time we run this some global state will be allocated which
+# shows up as memory that is allocated, but not released. So, run the
+# test once and discard the result.
+test(True)
+
+# Now run the test twice, the first time we clear our global reference
+# to the memory buffer object, which should allow Python to deallocate
+# the object. The second time we hold onto the global reference,
+# preventing Python from performing the deallocation.
+bytes_with_clear = test(True)
+bytes_without_clear = test(False)
+
+# The bug that used to exist in GDB was that even when we released the
+# global reference the gdb.Inferior object would not be deallocated.
+if bytes_with_clear > 0:
+ raise gdb.GdbError("memory leak when memory buffer should be released")
+if bytes_without_clear == 0:
+ raise gdb.GdbError("memory buffer object is no longer allocated")
+
+# Print a PASS message that the test script can see.
+print("PASS")