diff options
Diffstat (limited to 'gdb/testsuite/gdb.python/py-inferior-leak.py')
-rw-r--r-- | gdb/testsuite/gdb.python/py-inferior-leak.py | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.python/py-inferior-leak.py b/gdb/testsuite/gdb.python/py-inferior-leak.py new file mode 100644 index 0000000..914fb3e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-inferior-leak.py @@ -0,0 +1,109 @@ +# Copyright (C) 2021 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 tracemalloc +import gdb +import re + +# A global variable in which we store a reference to the gdb.Inferior +# object sent to us in the new_inferior event. +inf = None + +# Register the new_inferior event handler. +def new_inferior_handler(event): + global inf + inf = event.inferior + + +gdb.events.new_inferior.connect(new_inferior_handler) + +# A global filters list, we only care about memory allocations +# originating from this script. +filters = [tracemalloc.Filter(True, "*py-inferior-leak.py")] + +# Add a new inferior, and return the number of the new inferior. +def add_inferior(): + output = gdb.execute("add-inferior", False, True) + m = re.search(r"Added inferior (\d+)", output) + if m: + num = int(m.group(1)) + else: + raise RuntimeError("no match") + return num + + +# 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, inf + + # Start tracing, and take a snapshot of the current allocations. + tracemalloc.start() + snapshot1 = tracemalloc.take_snapshot() + + # Create an inferior, this triggers the new_inferior event, which + # in turn holds a reference to the new gdb.Inferior object in the + # global INF variable. + num = add_inferior() + gdb.execute("remove-inferiors %s" % num) + + # Possibly clear the global INF variable. + if clear: + inf = 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 deallocated 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 gdb.Inferior 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 gdb.Inferior should be released") +if bytes_without_clear == 0: + raise gdb.GdbError("gdb.Inferior object is no longer allocated") + +# Print a PASS message that the test script can see. +print("PASS") |