diff options
Diffstat (limited to 'gdb/testsuite/gdb.python')
-rw-r--r-- | gdb/testsuite/gdb.python/py-read-memory-leak.c | 27 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-read-memory-leak.exp | 44 | ||||
-rw-r--r-- | gdb/testsuite/gdb.python/py-read-memory-leak.py | 92 |
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") |