diff options
Diffstat (limited to 'gdb/testsuite/gdb.python/py-read-memory-leak.py')
-rw-r--r-- | gdb/testsuite/gdb.python/py-read-memory-leak.py | 92 |
1 files changed, 92 insertions, 0 deletions
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") |