From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 6E8243858C2F for ; Fri, 27 Jan 2023 16:34:12 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6E8243858C2F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1674837252; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=H8tbuNcicnZIv88O1nozCkz1phWN3lvs+Q1h6pSe8LU=; b=OntZ4h6AeeB0xYOtOuN52+UPsuq0jRL0Vmh954c44bW1fwnMMfAyd6cAKeZZRwCcgZ81Uz o6OQmxTMQLbdZXZjya4HeVGegBthTo/YP26uuhDIS66og6HI8ItwmO0C29QPdmRvApEXpH /jnSDjgGyoIFrOSo5/dAXBBjxOd/4GQ= Received: from mail-qt1-f200.google.com (mail-qt1-f200.google.com [209.85.160.200]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_128_GCM_SHA256) id us-mta-148-Yo-doLvwMx-sHw8J106sbQ-1; Fri, 27 Jan 2023 11:34:11 -0500 X-MC-Unique: Yo-doLvwMx-sHw8J106sbQ-1 Received: by mail-qt1-f200.google.com with SMTP id j14-20020ac874ce000000b003b6917d0731so2338159qtr.4 for ; Fri, 27 Jan 2023 08:34:11 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=H8tbuNcicnZIv88O1nozCkz1phWN3lvs+Q1h6pSe8LU=; b=RNJ8Hpzs0JSY4T+xoUwZjB0FdmE+poQyjU3ptBUd+jF5CeJ7YEfmpgfpNP/TK3wIyF jsHtu3ilPcnTfbUydb8IGMV9Cpsf8zS49FHNmmo2U8Ag5O8L4nY85x0QzfDZjW5JoEhB elSeY/zg73WRNardfIVFhPBMB4HIn2+q5y1t6lNk6//uiWbQ8+geyJwlkmR3NWfdLbre IJRqqyoDMXqwybH8DGjcYHVtvJAlFAHdY89cpc55eJznR51NtxEO700UlDrowW6pXLTJ T5nr9IGLNJIcO9E5UI9Bs1VVNc503EF+6LgXL+kZmG2NffsAN9PmigGVDimNy9kxDcgt 1Xqg== X-Gm-Message-State: AFqh2kqIRDEQa4oMFSoQC1U3SmPaB6Ln4gpsS0TVvwksv/Qu6gC6ps/y ZvlFe8CX8moUndKWE2QJkjxKmwoIWAnllvhxycjkYkpzzictIfoiB1J26Atg6jILfQAHBpSrUS9 QbepQyNojaDMm+SpBmVukW04SloXyB18UsR2d46nnq6t+GUN0AF7PR6ibYfXE83SXKCEy43pmtw == X-Received: by 2002:ac8:12c2:0:b0:3a9:90d9:58be with SMTP id b2-20020ac812c2000000b003a990d958bemr58730752qtj.56.1674837250416; Fri, 27 Jan 2023 08:34:10 -0800 (PST) X-Google-Smtp-Source: AMrXdXtqzNfALgxedW+rdyYRcAqCFlmSAX5zTjFIv1nE4TY8TCStfSOfDnmjrT6v0Gq5ye/V2YEW1Q== X-Received: by 2002:ac8:12c2:0:b0:3a9:90d9:58be with SMTP id b2-20020ac812c2000000b003a990d958bemr58730728qtj.56.1674837250064; Fri, 27 Jan 2023 08:34:10 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id s39-20020a05622a1aa700b003a7e38055c9sm2943217qtc.63.2023.01.27.08.34.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Jan 2023 08:34:09 -0800 (PST) From: Andrew Burgess To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCHv3 1/3] gdb/python: allow Python TUI windows to be replaced Date: Fri, 27 Jan 2023 16:34:03 +0000 Message-Id: <7fefb92241aed87ddf8c6e61183fd629c622d819.1674837139.git.aburgess@redhat.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: The documentation for gdb.register_window_type says: "... It's an error to try to replace one of the built-in windows, but other window types can be replaced. ..." I take this to mean that if I imported a Python script like this: gdb.register_window_type('my_window', FactoryFunction) Then GDB would have a new TUI window 'my_window', which could be created by calling FactoryFunction(). If I then, in the same GDB session imported a script which included: gdb.register_window_type('my_window', UpdatedFactoryFunction) Then GDB would replace the old 'my_window' factory with my new one, GDB would now call UpdatedFactoryFunction(). This is pretty useful in practice, as it allows users to iterate on their window implementation within a single GDB session. However, right now, this is not how GDB operates. The second call to register_window_type is basically ignored and the old window factory is retained. This is because in tui_register_window (tui/tui-layout.c) we use std::unordered_map::emplace to insert the new factory function, and emplace doesn't replace an existing element in an unordered_map. In this commit, before the emplace call, I now search for an already existing element, and delete any matching element from the map, the emplace call will then add the new factory function. --- .../gdb.python/tui-window-factory.exp | 73 +++++++++++++++++++ .../gdb.python/tui-window-factory.py | 48 ++++++++++++ gdb/tui/tui-layout.c | 8 ++ 3 files changed, 129 insertions(+) create mode 100644 gdb/testsuite/gdb.python/tui-window-factory.exp create mode 100644 gdb/testsuite/gdb.python/tui-window-factory.py diff --git a/gdb/testsuite/gdb.python/tui-window-factory.exp b/gdb/testsuite/gdb.python/tui-window-factory.exp new file mode 100644 index 00000000000..99f9fbb1bc4 --- /dev/null +++ b/gdb/testsuite/gdb.python/tui-window-factory.exp @@ -0,0 +1,73 @@ +# Copyright (C) 2023 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 . + +# Test that GDB correctly deallocates the window factory object (a) +# when a window factory is replaced, and (b) during GDB shutdown. +# +# This test also ensures that when a new window is registered (via the +# Python API) with the same name as an existing window, then the +# previous window is replaced. + +load_lib gdb-python.exp + +tuiterm_env + +clean_restart + +require allow_tui_tests allow_python_tests + +set pyfile [gdb_remote_download host \ + ${srcdir}/${subdir}/${gdb_test_file_name}.py] + +Term::clean_restart 24 80 +Term::prepare_for_tui + +gdb_test "source ${pyfile}" "Python script imported" \ + "import python scripts" + +gdb_test "python register_window_factory('msg_1')" \ + "Entering TestWindowFactory\\.__init__: msg_1" + +gdb_test "python register_window_factory('msg_2')" \ + [multi_line \ + "Entering TestWindowFactory\\.__init__: msg_2" \ + "Entering TestWindowFactory\\.__del__: msg_1"] + +gdb_test_no_output "tui new-layout test test_window 1 cmd 1 status 1" + +# Load the custom window layout and ensure that the correct window +# factory was used. +with_test_prefix "msg_2" { + Term::command_no_prompt_prefix "layout test" + Term::check_box_contents "check test_window box" 0 0 80 15 \ + "TestWindow \\(msg_2\\)" +} + +# Replace the existing window factory with a new one, then switch +# layouts so that GDB recreates the window, and check that the new +# window factory was used. +with_test_prefix "msg_3" { + Term::command "python register_window_factory('msg_3')" + Term::check_region_contents "check for python output" \ + 0 18 80 2 \ + [multi_line \ + "Entering TestWindowFactory.__init__: msg_3\\s+" \ + "Entering TestWindowFactory.__del__: msg_2"] + Term::command "layout src" + Term::command "layout test" + + Term::check_box_contents "check test_window box" 0 0 80 15 \ + "TestWindow \\(msg_3\\)" +} diff --git a/gdb/testsuite/gdb.python/tui-window-factory.py b/gdb/testsuite/gdb.python/tui-window-factory.py new file mode 100644 index 00000000000..d1254e7e3a0 --- /dev/null +++ b/gdb/testsuite/gdb.python/tui-window-factory.py @@ -0,0 +1,48 @@ +# Copyright (C) 2023 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 . + + +class TestWindow: + def __init__(self, tui_win, msg): + self.msg = msg + self.tui_win = tui_win + print("Entering TestWindow.__init__: %s" % self.msg) + + def render(self): + self.tui_win.erase() + self.tui_win.write("TestWindow (%s)" % self.msg) + + def __del__(self): + print("Entering TestWindow.__del__: %s" % self.msg) + + +class TestWindowFactory: + def __init__(self, msg): + self.msg = msg + print("Entering TestWindowFactory.__init__: %s" % self.msg) + + def __call__(self, tui_win): + print("Entering TestWindowFactory.__call__: %s" % self.msg) + return TestWindow(tui_win, self.msg) + + def __del__(self): + print("Entering TestWindowFactory.__del__: %s" % self.msg) + + +def register_window_factory(msg): + gdb.register_window_type("test_window", TestWindowFactory(msg)) + + +print("Python script imported") diff --git a/gdb/tui/tui-layout.c b/gdb/tui/tui-layout.c index ecdcd4884a7..ba7ec89973e 100644 --- a/gdb/tui/tui-layout.c +++ b/gdb/tui/tui-layout.c @@ -427,6 +427,14 @@ tui_register_window (const char *name, window_factory &&factory) if (!ISALPHA (name_copy[0])) error (_("window name must start with a letter, not '%c'"), name_copy[0]); + /* We already check above for all the builtin window names. If we get + this far then NAME must be a user defined window. Remove any existing + factory and replace it with this new version. */ + + auto iter = known_window_types->find (name); + if (iter != known_window_types->end ()) + known_window_types->erase (iter); + known_window_types->emplace (std::move (name_copy), std::move (factory)); } -- 2.25.4