From patchwork Fri Mar 10 14:55:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66215 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 01369385700E for ; Fri, 10 Mar 2023 14:56:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 01369385700E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460181; bh=c4STAhTEjZd8AfVjPljBP5zIqFnHloOcQxhK8IlfXb4=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=IRCTiax2Tzj6xtOFWxbIX7KNYEg7pHpl2EGqH5cK6X03IciU9pG9AC7S7waKPSQJ8 iKSuKWwZ22Ce+xzZ4e98uFly7h3noapeba3w9H+StIU2UeWXb4BCvZKEwjH13qL4ZA 51JasfWkNDmBFbEukYV0m7Z0ZrpQLtGM543elG4c= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 8851D3858D38 for ; Fri, 10 Mar 2023 14:55:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8851D3858D38 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-253-6q_3S5_dO86xHIaEDcfLVg-1; Fri, 10 Mar 2023 09:55:53 -0500 X-MC-Unique: 6q_3S5_dO86xHIaEDcfLVg-1 Received: by mail-wm1-f72.google.com with SMTP id k26-20020a05600c0b5a00b003dfe4bae099so2085899wmr.0 for ; Fri, 10 Mar 2023 06:55:53 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460152; 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=c4STAhTEjZd8AfVjPljBP5zIqFnHloOcQxhK8IlfXb4=; b=lFKYY+NHqUtFlbznbY1BQTzAljGGMQOuHYFtQl1Y++6pv614pEhheia6TboV2NDYHl DSNfYp3wjS9MIVq7r0jj+OCmoTDzyK+OA97MnTjK1XBgL5A9WFXP7UeZXirwIFD6HW4O kCc9UD8odAKrY3n63uhuH9Kf2OTfY03i5W1X+FdhqNyAdkt/k8a7x/hM7S7Pg91g3h6x 2tvDkY3PZ/Fs0FJNsuzexu7LGg/7/Z4+CaQfv3c7R6Y5/7p1J7lkz+pZ/IsfY9tT3Vfb owI+XN4v/17YCtraV5OpShJBy8tKK3he1DKrL6DLaai3QQXTIHCDM+zh0daK/aCOouzR 1Leg== X-Gm-Message-State: AO0yUKXziLlLxTqgjrLYVnDWj7OHSAID2/xl1dqwPsD7XZd2Ww3IYUFO HK0nkEw3KMVWYxX8JafB4wabQnbg/DsMoncjbSxOaKF+iUbTMmI/nDFXJXPzj75pq67qcKX4rQy /3V42pKEqErPSCHtxNBwKi0v16yPsZjf8qwxiBEhAuAe+brTU45Vtr91azhuXB+8NtDLiCMacdp 3PLVq+Dw== X-Received: by 2002:a05:600c:4f08:b0:3e2:20c7:6544 with SMTP id l8-20020a05600c4f0800b003e220c76544mr3010307wmq.19.1678460152583; Fri, 10 Mar 2023 06:55:52 -0800 (PST) X-Google-Smtp-Source: AK7set/58XTaL0D7NJ0fHfY4ZjLsnDkmaIv9Zq31YuODXSMV0ssgSv7UpG3X+XGyLvpa1Ct8TxAnxw== X-Received: by 2002:a05:600c:4f08:b0:3e2:20c7:6544 with SMTP id l8-20020a05600c4f0800b003e220c76544mr3010288wmq.19.1678460152233; Fri, 10 Mar 2023 06:55:52 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id p10-20020a1c544a000000b003eb2e685c7dsm154854wmi.9.2023.03.10.06.55.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:51 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 01/10] gdb/doc: spring clean the Python unwinders documentation Date: Fri, 10 Mar 2023 14:55:18 +0000 Message-Id: <3bad3bab1077f38d3f508e0cb4a071663e745b06.1678460067.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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" The documentation for the Python Unwinders API could do with some improvement. The 'Unwinder Skeleton Code' has an error: it says 'unwinders' when it should say 'unwinder' in one case. Additionally, by placing the 'Unwinder Skeleton Code' before the section 'Registering an Unwinder' we have skipping including the registration line in the skeleton code. But this is confusion for users (I think) as the skeleton code is almost complete, except for one missing line which the user has to figure out for themselves. By reordering the sections, it is now obvious that the registration should be included in the skeleton code, and the example is therefore almost complete. Additionally, in the example skeleton code the way in which the frame-id was being built (using the current stack point and program counter is (a) not correct, and (b) counter to what is laid out in the 'Unwinder Input' section when describing building a frame-id. I've removed the incorrect code and replaced it with more generic comments indicating what needs to be done. As the actual actions that need to be performed are both architecture specific, and dependent on the function being unwound, it's almost impossible to include more exact code here, but I think what I'm proposing is less misleading than what we had before. I've also added more cross references. Reviewed-By: Eli Zaretskii --- gdb/doc/python.texi | 82 ++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index b04f1de2ddf..9ab0aca7d57 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2784,9 +2784,10 @@ @defun PendingFrame.create_unwind_info (frame_id) Returns a new @code{gdb.UnwindInfo} instance identified by given -@var{frame_id}. The argument is used to build @value{GDBN}'s frame ID -using one of functions provided by @value{GDBN}. @var{frame_id}'s attributes -determine which function will be used, as follows: +@var{frame_id}. The @var{frame_id} is used internally by @value{GDBN} +to identify the frames within the current thread's stack. The +attributes of @var{frame_id} determine what type of frame ID is +created within @value{GDBN}: @table @code @item sp, pc @@ -2841,6 +2842,34 @@ @var{value} is a register value (a @code{gdb.Value} object). @end defun +@subheading Registering an Unwinder + +Object files and program spaces can have unwinders registered with +them. In addition, you can also register unwinders globally. + +The @code{gdb.unwinders} module provides the function to register an +unwinder: + +@defun gdb.unwinder.register_unwinder (locus, unwinder, replace=False) +@var{locus} specifies to which unwinder list to prepend the +@var{unwinder}. It can be either an object file (@pxref{Objfiles In +Python}), a program space (@pxref{Progspaces In Python}), or +@code{None}, in which case the unwinder is registered globally. The +newly added @var{unwinder} will be called before any other unwinder +from the same locus. Two unwinders in the same locus cannot have the +same name. An attempt to add an unwinder with an already existing +name raises an exception unless @var{replace} is @code{True}, in which +case the old unwinder is deleted and the new unwinder is registered in +its place. +@end defun + +@subheading Unwinder Precedence + +@value{GDBN} first calls the unwinders from all the object files in no +particular order, then the unwinders from the current program space, +then the globally registered unwinders, and finally the unwinders +builtin to @value{GDBN}. + @subheading Unwinder Skeleton Code @value{GDBN} comes with the module containing the base @code{Unwinder} @@ -2848,7 +2877,7 @@ follows: @smallexample -from gdb.unwinders import Unwinder +from gdb.unwinder import Unwinder class FrameId(object): def __init__(self, sp, pc): @@ -2857,53 +2886,30 @@ class MyUnwinder(Unwinder): - def __init__(....): - super(MyUnwinder, self).__init___() + def __init__(self): + super().__init___("MyUnwinder_Name") - def __call__(pending_frame): + def __call__(self, pending_frame): if not : return None - # Create UnwindInfo. Usually the frame is identified by the stack - # pointer and the program counter. - sp = pending_frame.read_register() - pc = pending_frame.read_register() + + # Create a FrameID. Usually the frame is identified by a + # stack pointer and the function address. + sp = ... compute a stack address ... + pc = ... compute function address ... unwind_info = pending_frame.create_unwind_info(FrameId(sp, pc)) - # Find the values of the registers in the caller's frame and + # Find the values of the registers in the caller's frame and # save them in the result: - unwind_info.add_saved_register(, ) + unwind_info.add_saved_register(, ) .... # Return the result: return unwind_info +gdb.unwinder.register_unwinder(, MyUnwinder(), ) @end smallexample -@subheading Registering an Unwinder - -Object files and program spaces can have unwinders registered with -them. In addition, you can also register unwinders globally. - -The @code{gdb.unwinders} module provides the function to register an -unwinder: - -@defun gdb.unwinder.register_unwinder (locus, unwinder, replace=False) -@var{locus} specifies to which unwinder list to prepend the -@var{unwinder}. It can be either an object file, a program space, or -@code{None}, in which case the unwinder is registered globally. The -newly added @var{unwinder} will be called before any other unwinder from the -same locus. Two unwinders in the same locus cannot have the same -name. An attempt to add an unwinder with an already existing name raises -an exception unless @var{replace} is @code{True}, in which case the -old unwinder is deleted. -@end defun - -@subheading Unwinder Precedence - -@value{GDBN} first calls the unwinders from all the object files in no -particular order, then the unwinders from the current program space, -and finally the unwinders from @value{GDBN}. - @node Xmethods In Python @subsubsection Xmethods In Python @cindex xmethods in Python From patchwork Fri Mar 10 14:55:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66217 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EFB4D385B524 for ; Fri, 10 Mar 2023 14:56:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org EFB4D385B524 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460199; bh=oLkhZKxTnU3zAmvVM8Rh9QQsmpp/Z0REKN6rd5dRBmw=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=YCbnqlC92/YIxBzSjSfO0yrc37BTiUaej2imiDVOJn4ENAa4Il9A4yRvdhKWJFpK5 oA+enJzZ/pWMjco+pjx7BdxB+v4Djac+jB2CFEhW+7iiVeTHWoUxkbm0Hd6Dj91GG6 OaBfPomSFjTahBFtDxMlm0UnSGnQGAgjr9lnxEYk= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id A73D13858D3C for ; Fri, 10 Mar 2023 14:55:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A73D13858D3C Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-573--4IYlmicOLGvXXESTqBFTA-1; Fri, 10 Mar 2023 09:55:55 -0500 X-MC-Unique: -4IYlmicOLGvXXESTqBFTA-1 Received: by mail-wm1-f69.google.com with SMTP id k20-20020a05600c1c9400b003e2249bd2b4so1917687wms.5 for ; Fri, 10 Mar 2023 06:55:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460154; 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=oLkhZKxTnU3zAmvVM8Rh9QQsmpp/Z0REKN6rd5dRBmw=; b=wZ4kf/BSxg980UgPAZ66hj/rfjXITnIO/sCB5SEg2smw88noUhoiQ21KcvLaZiARyh QggDrPfUiTB8TeI/k4Y7iXkZGuV8Jadauewyk2Lp3ErYl4gRfb5s5rOnyxoqF3Csfu4q Eo0TzNwMF6x8/PbIHz+EqOqA387VpBwjKD+cmLMdHLY1Y9CfCZFoe0it2bOZIfP2KxvK qwqxDIPJwW9IOMCfnEcKjdOJIuJw7aEYTM/ojxD/nuHIvRCfqfv32IAayDku7YfIev0t sfvJw5cBw6Zk7b3CIVW8qQTZF3xR1AXpvTxfoOsIuej2QGxUWsMAFzvEEDg9wO0fzGoW T99w== X-Gm-Message-State: AO0yUKVOsjh6G39WoOOyPkSaEKSQgTlv+CZJQFvcovab4456Xvv1j7dC tDlfUCXi/1Mi6Mh/LqjgLRGad8szEid80JfpuQZw7X+rS+pIhrQtXC9GE37Qzv2lNUpx8r03Ucw ZkQ8BYWFZ9opJJAv6/02Wq0hzAtxD8llAbaE8vXm+UD4QqFzO4MIdppwcGrec0zVvCfER58dtzi nXNORbFA== X-Received: by 2002:a5d:664e:0:b0:2c5:3ccf:e99a with SMTP id f14-20020a5d664e000000b002c53ccfe99amr17257934wrw.6.1678460153720; Fri, 10 Mar 2023 06:55:53 -0800 (PST) X-Google-Smtp-Source: AK7set+/YnvAlx/1fcnd/P2R0WrQA3Jby9Td9LZox2+sn7meuscn1LLnTFPJ/djCnjnmE5vvQ8HUCA== X-Received: by 2002:a5d:664e:0:b0:2c5:3ccf:e99a with SMTP id f14-20020a5d664e000000b002c53ccfe99amr17257919wrw.6.1678460153393; Fri, 10 Mar 2023 06:55:53 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id w1-20020a5d6081000000b002c6e8cb612fsm2236191wrt.92.2023.03.10.06.55.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:53 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 02/10] gdb/python: make the gdb.unwinder.Unwinder class more robust Date: Fri, 10 Mar 2023 14:55:19 +0000 Message-Id: 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 X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP, URI_LONG_REPEAT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" This commit makes a few related changes to the gdb.unwinder.Unwinder class attributes: 1. The 'name' attribute is now a read-only attribute. This prevents user code from changing the name after registering the unwinder. It seems very unlikely that any user is actually trying to do this in the wild, so I'm not very worried that this will upset anyone, 2. We now validate that the name is a string in the Unwinder.__init__ method, and throw an error if this is not the case. Hopefully nobody was doing this in the wild. This should make it easier to ensure the 'info unwinder' command shows sane output (how to display a non-string name for an unwinder?), 3. The 'enabled' attribute is now implemented with a getter and setter. In the setter we ensure that the new value is a boolean, but the real important change is that we call 'gdb.invalidate_cached_frames()'. This means that the backtrace will be updated if a user manually disables an unwinder (rather than calling the 'disable unwinder' command). It is not unreasonable to think that a user might register multiple unwinders (relating to some project) and have one command that disables/enables all the related unwinders. This command might operate by poking the enabled attribute of each unwinder object directly, after this commit, this would now work correctly. There's tests for all the changes, and lots of documentation updates that both cover the new changes, but also further improve (I think) the general documentation for GDB's Unwinder API. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 14 ++++ gdb/doc/python.texi | 60 +++++++++++++++- gdb/python/lib/gdb/unwinder.py | 23 ++++++- gdb/testsuite/gdb.python/py-unwind.exp | 94 ++++++++++++++++++++++++-- gdb/testsuite/gdb.python/py-unwind.py | 13 +++- 5 files changed, 191 insertions(+), 13 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index c32ff92c98a..ed0f86e79ec 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -96,6 +96,20 @@ show always-read-ctf without a thread restriction. The same is also true for the 'task' field of an Ada task-specific breakpoint. +* Python API + + ** The gdb.unwinder.Unwinder.name attribute is now read-only. + + ** The name argument passed to gdb.unwinder.Unwinder.__init__ must + now be of type 'str' otherwise a TypeError will be raised. + + ** The gdb.unwinder.Unwinder.enabled attribute can now only accept + values of type 'bool'. Changing this attribute will now + invalidate GDB's frame-cache, which means GDB will need to + rebuild its frame-cache when next required - either with, or + without the particular unwinder, depending on how 'enabled' was + changed. + *** Changes in GDB 13 * MI version 1 is deprecated, and will be removed in GDB 14. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 9ab0aca7d57..c36130210ba 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2842,6 +2842,33 @@ @var{value} is a register value (a @code{gdb.Value} object). @end defun +@subheading The @code{gdb.unwinder} Module + +@value{GDBN} comes with a @code{gdb.unwinder} module which contains +the following class: + +@deftp {class} gdb.unwinder.Unwinder +The @code{Unwinder} class is a base class from which user created +unwinders can derive, though it is not required that unwinders derive +from this class, so long any user created unwinder has the required +@code{name} and @code{enabled} attributes. + +@defun gdb.unwinder.Unwinder.__init__(@var{name}) +The @var{name} is a string used to identify this unwinder within some +@value{GDBN} commands. +@end defun + +@defvar gdb.unwinder.name +A read-only attribute which is a string, the name of this unwinder. +@end defvar + +@defvar gdb.unwinder.enabled +A modifiable attribute containing a boolean, when @code{True} the +unwinder is enabled, and will be used by @value{GDBN}. When +@code{False} the unwinder has been disabled, and will not be used. +@end defvar +@end deftp + @subheading Registering an Unwinder Object files and program spaces can have unwinders registered with @@ -2872,9 +2899,7 @@ @subheading Unwinder Skeleton Code -@value{GDBN} comes with the module containing the base @code{Unwinder} -class. Derive your unwinder class from it and structure the code as -follows: +Here is an example of how and structure a user created unwinder: @smallexample from gdb.unwinder import Unwinder @@ -2910,6 +2935,35 @@ gdb.unwinder.register_unwinder(, MyUnwinder(), ) @end smallexample +@subheading Managing Registered Unwinders +@value{GDBN} defines 3 new commands to manage registered unwinders. +These are: + +@table @code +@item info unwinder @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]} +Lists all registered unwinders. Arguments @var{locus} and +@var{name-regexp} are both optional and can be used to filter which +unwinders are listed. + +The @var{locus} argument should be either @kbd{global}, +@kbd{progspace}, or the name of an object file. Only unwinders +registered for the specified locus will be listed. + +The @var{name-regexp} is a regular expression used to match against +unwinder names. When trying to match against unwinder names that +include a string enclose @var{name-regexp} in quotes. +@item disable unwinder @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]} +The @var{locus} and @var{name-regexp} are interpreted as in @kbd{info +unwinder} above, but instead of listing the matching unwinders, all of +the matching unwinders are disabled. The @code{enabled} field of each +matching unwinder is set to @code{False}. +@item enable unwinder @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]} +The @var{locus} and @var{name-regexp} are interpreted as in @kbd{info +unwinder} above, but instead of listing the matching unwinders, all of +the matching unwinders are enabled. The @code{enabled} field of each +matching unwinder is set to @code{True}. +@end table + @node Xmethods In Python @subsubsection Xmethods In Python @cindex xmethods in Python diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py index a854d8dbdaa..1303245c054 100644 --- a/gdb/python/lib/gdb/unwinder.py +++ b/gdb/python/lib/gdb/unwinder.py @@ -35,8 +35,27 @@ class Unwinder(object): Args: name: An identifying name for the unwinder. """ - self.name = name - self.enabled = True + + if not isinstance(name, str): + raise TypeError("incorrect type for name: %s" % type(name)) + + self._name = name + self._enabled = True + + @property + def name(self): + return self._name + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, value): + if not isinstance(value, bool): + raise TypeError("incorrect type for enabled attribute: %s" % type(value)) + self._enabled = value + gdb.invalidate_cached_frames() def __call__(self, pending_frame): """GDB calls this method to unwind a frame. diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index 5bf9ae129ac..337e5dc2504 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -40,25 +40,105 @@ if {![runto_main]} { return 0 } +# Check for the corrupt backtrace. +proc check_for_broken_backtrace {testname} { + gdb_test_sequence "where" $testname { + "\\r\\n#0 .* corrupt_frame_inner \\(\\) at " + "\\r\\n#1 .* corrupt_frame_outer \\(\\) at " + "Backtrace stopped: frame did not save the PC" + } +} + +# Check for the correct backtrace. +proc check_for_fixed_backtrace {testname} { + gdb_test_sequence "where" $testname { + "\\r\\n#0 .* corrupt_frame_inner \\(\\) at " + "\\r\\n#1 .* corrupt_frame_outer \\(\\) at " + "\\r\\n#2 .* main \\(.*\\) at" + } +} + +# Check the 'info unwinder' output. +proc check_info_unwinder {testname enabled} { + if {$enabled} { + set suffix "" + } else { + set suffix " \\\[disabled\\\]" + } + + gdb_test_sequence "info unwinder" $testname \ + [list \ + "Global:" \ + " test unwinder${suffix}"] +} + set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] gdb_breakpoint [gdb_get_line_number "break backtrace-broken"] +gdb_continue_to_breakpoint "break backtrace-broken" + +check_for_broken_backtrace "Backtrace is initially broken" + gdb_test "source ${pyfile}" "Python script imported" \ - "import python scripts" + "import python scripts" -gdb_continue_to_breakpoint "break backtrace-broken" -gdb_test_sequence "where" "Backtrace restored by unwinder" { - "\\r\\n#0 .* corrupt_frame_inner \\(\\) at " - "\\r\\n#1 .* corrupt_frame_outer \\(\\) at " - "\\r\\n#2 .* main \\(.*\\) at" -} +check_info_unwinder "info unwinder after loading script" on + +check_for_fixed_backtrace "check backtrace after loading unwinder" # Check that the Python unwinder frames can be flushed / released. gdb_test "maint flush register-cache" "Register cache flushed\\." "flush frames" +check_for_fixed_backtrace "check backtrace after flush" + +# Try to disable the unwinder but instead set the enabled field to a +# non boolean value. This should fail. Check the 'info unwinder' +# output to be sure. +gdb_test "python global_test_unwinder.enabled = \"off\"" \ + [multi_line \ + "TypeError: incorrect type for enabled attribute: " \ + "Error while executing Python code\\."] +check_info_unwinder "info unwinder after failed disable" on + +# While we're doing silly stuff, lets try to change the name of this +# unwider. Doing this is bad as the new name might clash with an +# already registered name, which violates the promises made during +# 'register_unwinder'. +gdb_test "python global_test_unwinder.name = \"foo\"" \ + [multi_line \ + "AttributeError: can't set attribute" \ + "Error while executing Python code\\."] +check_info_unwinder "info unwinder after failed name change" on + +# Now actually disable the unwinder by manually adjusting the +# 'enabled' attribute. Check that the stack is once again broken, and +# that the unwinder shows as disabled in the 'info unwinder' output. +gdb_test_no_output "python global_test_unwinder.enabled = False" +check_for_broken_backtrace "stack is broken after disabling" +check_info_unwinder "info unwinder after manually disabling" off + +# Now enable the unwinder using the 'enable unwinder' command. +gdb_test "enable unwinder global \"test unwinder\"" \ + "1 unwinder enabled" +check_for_fixed_backtrace "check backtrace after enabling with command" +check_info_unwinder "info unwinder after command enabled" on + +# And now disable using the command and check the stack is once again +# broken, and that the 'info unwinder' output updates correctly. +gdb_test "disable unwinder global \"test unwinder\"" \ + "1 unwinder disabled" +check_for_broken_backtrace "stack is broken after command disabling" +check_info_unwinder "info unwinder after command disabling" off + # Check that invalid register names cause errors. gdb_test "python print(add_saved_register_error)" "True" \ "add_saved_register error" gdb_test "python print(read_register_error)" "True" \ "read_register error" + +# Try to create an unwinder object with a non-string name. +gdb_test "python obj = simple_unwinder(True)" \ + [multi_line \ + "TypeError: incorrect type for name: " \ + "Error while executing Python code\\."] diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index f7ad8e45a35..edd2e30eb9b 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -130,5 +130,16 @@ class TestUnwinder(Unwinder): return None -gdb.unwinder.register_unwinder(None, TestUnwinder(), True) +global_test_unwinder = TestUnwinder() +gdb.unwinder.register_unwinder(None, global_test_unwinder, True) + + +class simple_unwinder(Unwinder): + def __init__(self, name): + super().__init__(name) + + def __call__(self, pending_frame): + return None + + print("Python script imported") From patchwork Fri Mar 10 14:55:20 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66216 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6CA0F385E839 for ; Fri, 10 Mar 2023 14:56:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6CA0F385E839 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460185; bh=ioh5OAVLr8e1z8vw5L+Wu00tynnGD5O71/9qSHjD+Jg=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=W0cBcH+Bp/8cCWrszyRFqxhUZfSzd/wm8xyJePZ/URxfG7oZmVazbY3aqFHYVBnrE MjPQIgiXk+coxSZ254vE597XRemM737Ot9igG7Ajgj8z1sJL5KZKbg2heovTSyt3WH sntI5hryd55lZ8lPcBzdOPus5OyynGgZM9sIP40A= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 A652A3858CDA for ; Fri, 10 Mar 2023 14:55:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A652A3858CDA Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-138-qceXRVyANYKQJc_uY4xzDw-1; Fri, 10 Mar 2023 09:55:56 -0500 X-MC-Unique: qceXRVyANYKQJc_uY4xzDw-1 Received: by mail-wr1-f69.google.com with SMTP id m10-20020adfe94a000000b002cdc5eac0d0so1111289wrn.2 for ; Fri, 10 Mar 2023 06:55:55 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460154; 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=ioh5OAVLr8e1z8vw5L+Wu00tynnGD5O71/9qSHjD+Jg=; b=ZG285R+aREZGWSGGgFfT7Q6iqVHn7jrF/H25jGm0vEXPrwjLGNtKf8XDXYz7vFctoe cDwfOUqhdebmgu73EWiwvZ3D4AbfSi6sSNi7r1xCONhvdvErwMuZd5HNE+1KRTGijK2b fvBL7jqsP832HG2vkaHY/ps7mLogeIEWMY09ODp4txrnmD/jNa1KH9l34W3AIKGdegtc UJnApF3rywjUhfOWr/RgQuzylpM4CqYe+hmxuvd+VjsqBtBbUocd5X1nX+Rmrb9QIqzc ZOLmm4z70PPX626sCnMrI0GkcBz1BhCkReSy5yf1NUhBmt1dluBmRmrsWO/tRAvAbHo2 1ulg== X-Gm-Message-State: AO0yUKXCPYEVGE1LU42jVVDMcCYWJVhLR6atSPot3mDhTJ8tzwJay7NR K2Ln4IHVaNwm/3k1UrkQKsE/Kth5Fx5GgnOlWgP0bzmbJzQn2D2wr5vWrq05IhPZuou0rtqqAZ9 SNfCc8okASEmbQvqSIF69b2GD2kAURbcYK1FiA35k7eGH3txmus+mN7QYkuUP0qJ29nJGparsy7 19NcsXcw== X-Received: by 2002:a05:600c:2150:b0:3e2:22f9:c4b9 with SMTP id v16-20020a05600c215000b003e222f9c4b9mr1600016wml.4.1678460154783; Fri, 10 Mar 2023 06:55:54 -0800 (PST) X-Google-Smtp-Source: AK7set/nrMecNX+ZYVravkiMEVqVEssvbDYDNdeY4VCG+7cgt5dDUWhBpPZyoIPG8aeEhkd6oZhhSw== X-Received: by 2002:a05:600c:2150:b0:3e2:22f9:c4b9 with SMTP id v16-20020a05600c215000b003e222f9c4b9mr1599997wml.4.1678460154483; Fri, 10 Mar 2023 06:55:54 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id p25-20020a05600c205900b003eaf666cbe0sm114871wmg.27.2023.03.10.06.55.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:54 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 03/10] gdb/python: remove unneeded nullptr check in frapy_block Date: Fri, 10 Mar 2023 14:55:20 +0000 Message-Id: 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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" Spotted a redundant nullptr check in python/py-frame.c in the function frapy_block. This was introduced in commit 57126e4a45e3000e when we expanded an earlier check in return early if the pointer in question is nullptr. There should be no user visible changes after this commit. --- gdb/python/py-frame.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c index ecd55d2e568..00cd4bee492 100644 --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -304,13 +304,7 @@ frapy_block (PyObject *self, PyObject *args) return NULL; } - if (block) - { - return block_to_block_object - (block, fn_block->function ()->objfile ()); - } - - Py_RETURN_NONE; + return block_to_block_object (block, fn_block->function ()->objfile ()); } From patchwork Fri Mar 10 14:55:21 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66219 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 969673850848 for ; Fri, 10 Mar 2023 14:57:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 969673850848 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460230; bh=TPpZrz7EKXI7yB3B/uBocCYr3+fL1IyJBVMIUmLjIaY=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=SHJm/9JnCbFyHkos/GZTBK/QNoZiFfstHB7hF4nYMxXndOkAkE/CdpqWpeZTlw7uW 90OjpnOKtSCY5HQxS7p1Q+UyiN0lw60GPgdYSGVCk4MeCwxjKHMctFedkhinxv6JZP fyqBX2wSWwM3nvwDKubYi6P13UR3bGrbVZA+raok= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 99E823858C5E for ; Fri, 10 Mar 2023 14:55:58 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 99E823858C5E Received: from mail-wr1-f69.google.com (mail-wr1-f69.google.com [209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-206-TCLsEMVZOkOb93XwAzo8cg-1; Fri, 10 Mar 2023 09:55:57 -0500 X-MC-Unique: TCLsEMVZOkOb93XwAzo8cg-1 Received: by mail-wr1-f69.google.com with SMTP id l5-20020a5d6745000000b002c91cb49949so1104860wrw.14 for ; Fri, 10 Mar 2023 06:55:57 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460156; 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=TPpZrz7EKXI7yB3B/uBocCYr3+fL1IyJBVMIUmLjIaY=; b=tnouVv84zpXLJ+JNx0c3crnPU161JegUjsvc3yQGcO8B8DnpDzHf1Y1lxauN13pnNo 0ekSNA/uqkKex0HYKkuQlcSC+LAiLuDwpkXwtiCRbz7APXvOLAyI6LPaDpWUTahlub41 4Uaj+QPl2Ukji2ecuiKBXxiDIwUgiZvw8ZSNlvs0A6Lr2varlh9TY6ouDj8hNOadmNyQ BNtAV29/VoI3kHR8BteNewTDhLPx4qDURgE4vby99M52ZJsDEAiDmE2EOFzx+g0FdMvc bIHd8isYhcE/84VAmjPh92lyhGvIsfEF5RhZFWDv4B+QdriLe0lu9sG2eNxVws+BrT6K 41cw== X-Gm-Message-State: AO0yUKWw55R/p+WYpoRyl6DO33O3Afhk95ABi6qj4PzKUlqEXsJPYrs2 xtDIC4HclV7n+8ut3vjLNGp65FG45bN/SYp98Bl3bJPbe3YPLV13wWny5Jc+AZYEzfcJeZmeRLW fT0mKoef+L/4C9Fv/1GIkw2+hxR1ITvyQoKLmsGpS2CCWzxQ8IeBjmaqvlQhH9xABoZ07kHkhjy nPukBr6g== X-Received: by 2002:a05:600c:4f02:b0:3eb:39c3:8844 with SMTP id l2-20020a05600c4f0200b003eb39c38844mr3095915wmq.12.1678460155961; Fri, 10 Mar 2023 06:55:55 -0800 (PST) X-Google-Smtp-Source: AK7set9S9YU/HUBsHEMrv71O8y6n6Q+m+kT00YYDPg9obqdGCHWnw5lBofsU9rsKgFrfMecJyramsg== X-Received: by 2002:a05:600c:4f02:b0:3eb:39c3:8844 with SMTP id l2-20020a05600c4f0200b003eb39c38844mr3095902wmq.12.1678460155691; Fri, 10 Mar 2023 06:55:55 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id q20-20020a1cf314000000b003ebff290a52sm106088wmq.28.2023.03.10.06.55.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:55 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 04/10] gdb/python: add PENDING_FRAMEPY_REQUIRE_VALID macro in py-unwind.c Date: Fri, 10 Mar 2023 14:55:21 +0000 Message-Id: <9633a6228844ef210896ea73246525b00e131e8e.1678460067.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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" This commit copies the pattern that is present in many other py-*.c files: having a single macro to check that the Python object is still valid. This cleans up the code a little throughout the py-unwind.c file. Some of the exception messages will change slightly with this commit, though the type of the exceptions is still ValueError in all cases. I started writing some tests for this change and immediately ran into a problem: GDB would crash. It turns out that the PendingFrame objects are not being marked as invalid! In pyuw_sniffer where the pending frames are created, we make use of a scoped_restore to invalidate the pending frame objects. However, this only restores the pending_frame_object::frame_info field to its previous value -- and it turns out we never actually give this field an initial value, it's left undefined. So, when the scoped_restore (called invalidate_frame) performs its cleanup, it actually restores the frame_info field to an undefined value. If this undefined value is not nullptr then any future accesses to the PendingFrame object result in undefined behaviour and most likely, a crash. As part of this commit I now initialize the frame_info field, which ensures all the new tests now pass. --- gdb/python/py-unwind.c | 53 +++++++++++++------------- gdb/testsuite/gdb.python/py-unwind.exp | 19 +++++++++ gdb/testsuite/gdb.python/py-unwind.py | 7 ++++ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index ee776bf8dea..12b14616363 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -52,6 +52,17 @@ show_pyuw_debug (struct ui_file *file, int from_tty, #define PYUW_SCOPED_DEBUG_ENTER_EXIT \ scoped_debug_enter_exit (pyuw_debug, "py-unwind") +/* Require a valid pending frame. */ +#define PENDING_FRAMEPY_REQUIRE_VALID(pending_frame) \ + do { \ + if ((pending_frame)->frame_info == nullptr) \ + { \ + PyErr_SetString (PyExc_ValueError, \ + _("gdb.PendingFrame is invalid.")); \ + return nullptr; \ + } \ + } while (0) + struct pending_frame_object { PyObject_HEAD @@ -215,21 +226,20 @@ unwind_infopy_str (PyObject *self) } /* Create UnwindInfo instance for given PendingFrame and frame ID. - Sets Python error and returns NULL on error. */ + Sets Python error and returns NULL on error. + + The PYO_PENDING_FRAME object must be valid. */ static PyObject * pyuw_create_unwind_info (PyObject *pyo_pending_frame, struct frame_id frame_id) { + gdb_assert (((pending_frame_object *) pyo_pending_frame)->frame_info + != nullptr); + unwind_info_object *unwind_info - = PyObject_New (unwind_info_object, &unwind_info_object_type); + = PyObject_New (unwind_info_object, &unwind_info_object_type); - if (((pending_frame_object *) pyo_pending_frame)->frame_info == NULL) - { - PyErr_SetString (PyExc_ValueError, - "Attempting to use stale PendingFrame"); - return NULL; - } unwind_info->frame_id = frame_id; Py_INCREF (pyo_pending_frame); unwind_info->pending_frame = pyo_pending_frame; @@ -365,15 +375,11 @@ static PyObject * pending_framepy_read_register (PyObject *self, PyObject *args) { pending_frame_object *pending_frame = (pending_frame_object *) self; + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + int regnum; PyObject *pyo_reg_id; - if (pending_frame->frame_info == NULL) - { - PyErr_SetString (PyExc_ValueError, - "Attempting to read register from stale PendingFrame"); - return NULL; - } if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id)) return NULL; if (!gdbpy_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num)) @@ -417,6 +423,8 @@ pending_framepy_create_unwind_info (PyObject *self, PyObject *args) CORE_ADDR pc; CORE_ADDR special; + PENDING_FRAMEPY_REQUIRE_VALID ((pending_frame_object *) self); + if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id)) return NULL; if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp)) @@ -451,12 +459,8 @@ pending_framepy_architecture (PyObject *self, PyObject *args) { pending_frame_object *pending_frame = (pending_frame_object *) self; - if (pending_frame->frame_info == NULL) - { - PyErr_SetString (PyExc_ValueError, - "Attempting to read register from stale PendingFrame"); - return NULL; - } + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + return gdbarch_to_arch_object (pending_frame->gdbarch); } @@ -467,12 +471,8 @@ pending_framepy_level (PyObject *self, PyObject *args) { pending_frame_object *pending_frame = (pending_frame_object *) self; - if (pending_frame->frame_info == NULL) - { - PyErr_SetString (PyExc_ValueError, - "Attempting to read stack level from stale PendingFrame"); - return NULL; - } + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + int level = frame_relative_level (pending_frame->frame_info); return gdb_py_object_from_longest (level).release (); } @@ -538,6 +538,7 @@ pyuw_sniffer (const struct frame_unwind *self, frame_info_ptr this_frame, return 0; } pfo->gdbarch = gdbarch; + pfo->frame_info = nullptr; scoped_restore invalidate_frame = make_scoped_restore (&pfo->frame_info, this_frame); diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index 337e5dc2504..3e214ee0f45 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -142,3 +142,22 @@ gdb_test "python obj = simple_unwinder(True)" \ [multi_line \ "TypeError: incorrect type for name: " \ "Error while executing Python code\\."] + +# Now register the simple_unwinder with a valid name, and use the +# unwinder to capture a PendingFrame object. +gdb_test_no_output "python obj = simple_unwinder(\"simple\")" +gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj)" +check_for_broken_backtrace "backtrace to capture a PendingFrame object" + +# Call methods on the captured gdb.PendingFrame and check we see the +# expected error. +gdb_test_no_output "python pf = captured_pending_frame" +foreach cmd {"pf.read_register(\"pc\")" \ + "pf.create_unwind_info(None)" \ + "pf.architecture()" \ + "pf.level()"} { + gdb_test "python $cmd" \ + [multi_line \ + "ValueError: gdb\\.PendingFrame is invalid\\." \ + "Error while executing Python code\\."] +} diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index edd2e30eb9b..b30e843e7e5 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -133,12 +133,19 @@ class TestUnwinder(Unwinder): global_test_unwinder = TestUnwinder() gdb.unwinder.register_unwinder(None, global_test_unwinder, True) +# This is filled in by the simple_unwinder class. +captured_pending_frame = None + class simple_unwinder(Unwinder): def __init__(self, name): super().__init__(name) def __call__(self, pending_frame): + global captured_pending_frame + + if captured_pending_frame is None: + captured_pending_frame = pending_frame return None From patchwork Fri Mar 10 14:55:22 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66222 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B9804385558F for ; Fri, 10 Mar 2023 14:57:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B9804385558F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460261; bh=JrcnAA+FYO9AbT/KhEhdAn4WI4ttcyo276Mmtlsa8r4=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=eBl732Uuw+XEwxuBh/pgVM+mpe6awDV7tVod0VEPXysdD5TJzkOP/R9MWYzk6ZWr2 er/Rruv1vhIPUdBq9EHegJg8chKHsQWf4Bs4SonEjmJQYRVOR+iX8jAzeJZZnWDlrt 4mQig2f/2soULP8VIovG6Ttolj3oDUrci/i9Nf/w= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 9C8273858C66 for ; Fri, 10 Mar 2023 14:56:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9C8273858C66 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-103--N97dj-yMpWP2Xs5JH52uA-1; Fri, 10 Mar 2023 09:55:59 -0500 X-MC-Unique: -N97dj-yMpWP2Xs5JH52uA-1 Received: by mail-wm1-f72.google.com with SMTP id az39-20020a05600c602700b003e97eb80524so3968672wmb.4 for ; Fri, 10 Mar 2023 06:55:58 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460157; 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=JrcnAA+FYO9AbT/KhEhdAn4WI4ttcyo276Mmtlsa8r4=; b=UNPvMRajYtQ3u3aEZsXqygkyXmyvWR2i5bsrsMpla6CtWksYDpF2eLdyU8rTKv+OMM 4vXB26MQicIq0FJilDd9p8oBbfjEwHclvNPSE3GJhd3gw14rai+TaomQSoMwvtm9ysh9 3zypI2vXBf0PC3EV+SQJhrv0ZvB6a/93XVcQXy+qqsYDbWKAumHdMCcraQ3VnMKAAgwB wyO7dXdKvTgmal87UYMqNk9Wo/FsRm9GSjCuCgSJnkXMYuMVkpA+A4x1FByzuyI/71mt r/KePSt6G85GlOzy44HWEAf8O0S6Cph5kJ3ZENN32vW7/Oztv1qCU25GXrvpWFkmfRHE 9F2Q== X-Gm-Message-State: AO0yUKW2Nye62UjDq52ZP/omVE4bOYqXNjz3aCzzGt1TQXJzOdQDd5kI HzxbrOXW2hLk1hVnRZgdhO5KFDs6c2N/4sR9jh/nGaXsBHocTJ4hjT0c2hvjl364n7Sc/+840ot 11+2LmQWUG5GG09C5ZcV0ILnUj40XSYUL0kRbPtkH/sDqmX8Bj23FF8iSEYs6hSmXfWDD7+2/M/ LIdeLSUg== X-Received: by 2002:a1c:7910:0:b0:3eb:39e2:9157 with SMTP id l16-20020a1c7910000000b003eb39e29157mr3081617wme.22.1678460157393; Fri, 10 Mar 2023 06:55:57 -0800 (PST) X-Google-Smtp-Source: AK7set+wDVL3Ps7mUTbgq2tx2aDWKOI5Bu1bOAF/sTNWIQdyxZLOJbyQIPtHsLbaFVvB8l/6HC1WDg== X-Received: by 2002:a1c:7910:0:b0:3eb:39e2:9157 with SMTP id l16-20020a1c7910000000b003eb39e29157mr3081593wme.22.1678460156794; Fri, 10 Mar 2023 06:55:56 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id m27-20020a05600c3b1b00b003e733a973d2sm88254wms.39.2023.03.10.06.55.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:56 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 05/10] gdb/python: add some additional methods to gdb.PendingFrame Date: Fri, 10 Mar 2023 14:55:22 +0000 Message-Id: <5bd7327ee61fd1634235cd4ffddbfbc91d392e03.1678460067.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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" The gdb.Frame class has far more methods than gdb.PendingFrame. Given that a PendingFrame hasn't yet been claimed by an unwinder, there is a limit to which methods we can add to it, but many of the methods that the Frame class has, the PendingFrame class could also support. In this commit I've added those methods to PendingFrame that I believe are safe. In terms of implementation: if I was starting from scratch then I would implement many of these (or most of these) as attributes rather than methods. However, given both Frame and PendingFrame are just different representation of a frame, I think there is value in keeping the interface for the two classes the same. For this reason everything here is a method -- that's what the Frame class does. The new methods I've added are: - gdb.PendingFrame.is_valid(): Return True if the pending frame object is valid. - gdb.PendingFrame.name(): Return the name for the frame's function, or None. - gdb.PendingFrame.pc(): Return the $pc register value for this frame. - gdb.PendingFrame.language(): Return a string containing the language for this frame, or None. - gdb.PendingFrame.find_sal(): Return a gdb.Symtab_and_line object for the current location within the pending frame, or None. - gdb.PendingFrame.block(): Return a gdb.Block for the current pending frame, or None. - gdb.PendingFrame.function(): Return a gdb.Symbol for the current pending frame, or None. In every case I've just copied the implementation over from gdb.Frame and cleaned the code slightly e.g. NULL to nullptr. Additionally each function required a small update to reflect the PendingFrame type, but that's pretty minor. There are tests for all the new methods. For more extensive testing, I added the following code to the file gdb/python/lib/command/unwinders.py: from gdb.unwinder import Unwinder class TestUnwinder(Unwinder): def __init__(self): super().__init__("XXX_TestUnwinder_XXX") def __call__(self,pending_frame): lang = pending_frame.language() try: block = pending_frame.block() assert isinstance(block, gdb.Block) except RuntimeError as rte: assert str(rte) == "Cannot locate block for frame." function = pending_frame.function() arch = pending_frame.architecture() assert arch is None or isinstance(arch, gdb.Architecture) name = pending_frame.name() assert name is None or isinstance(name, str) valid = pending_frame.is_valid() pc = pending_frame.pc() sal = pending_frame.find_sal() assert sal is None or isinstance(sal, gdb.Symtab_and_line) return None gdb.unwinder.register_unwinder(None, TestUnwinder()) This registers a global unwinder that calls each of the new PendingFrame methods and checks the result is of an acceptable type. The unwinder never claims any frames though, so shouldn't change how GDB actually behaves. I then ran the testsuite. There was only a single regression, a test that uses 'disable unwinder' and expects a single unwinder to be disabled -- the extra unwinder is now disabled too, which changes the test output. So I'm reasonably confident that the new methods are not going to crash GDB. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 20 +++ gdb/doc/python.texi | 40 +++++ gdb/python/py-unwind.c | 221 +++++++++++++++++++++++++ gdb/testsuite/gdb.python/py-unwind.exp | 33 +++- gdb/testsuite/gdb.python/py-unwind.py | 104 ++++++++++++ 5 files changed, 417 insertions(+), 1 deletion(-) diff --git a/gdb/NEWS b/gdb/NEWS index ed0f86e79ec..0d9049ff134 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -110,6 +110,26 @@ show always-read-ctf without the particular unwinder, depending on how 'enabled' was changed. + ** New methods added to the gdb.PendingFrame class. These methods + have the same behaviour as the corresponding methods on + gdb.Frame. The new methods are: + + - gdb.PendingFrame.name(): Return the name for the frame's + function, or None. + - gdb.PendingFrame.is_valid(): Return True if the pending frame + object is valid. + - gdb.PendingFrame.pc(): Return the $pc register value for this + frame. + - gdb.PendingFrame.language(): Return a string containing the + language for this frame, or None. + - gdb.PendingFrame.find_sal(): Return a gdb.Symtab_and_line + object for the current location within the pending frame, or + None. + - gdb.PendingFrame.block(): Return a gdb.Block for the current + pending frame, or None. + - gdb.PendingFrame.function(): Return a gdb.Symbol for the + current pending frame, or None. + *** Changes in GDB 13 * MI version 1 is deprecated, and will be removed in GDB 14. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index c36130210ba..1c4239841af 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2830,6 +2830,46 @@ @xref{Frames, ,Stack Frames}. @end defun +@defun PendingFrame.name () +Returns the function name of this pending frame, or @code{None} if it +can't be obtained. +@end defun + +@defun PendingFrame.is_valid () +Returns true if the @code{gdb.PendingFrame} object is valid, false if +not. A pending frame object becomes invalid when the call to the +unwinder, for which the pending frame was created, returns. + +All @code{gdb.PendingFrame} methods, except this one, will raise an +exception if the pending frame object is invalid at the time the +method is called. +@end defun + +@defun PendingFrame.pc () +Returns the pending frame's resume address. +@end defun + +@defun PendingFrame.block () +Return the pending frame's code block (@pxref{Blocks In Python}). If +the frame does not have a block -- for example, if there is no +debugging information for the code in question -- then this will raise +a @code{RuntimeError} exception. +@end defun + +@defun PendingFrame.function () +Return the symbol for the function corresponding to this pending frame. +@xref{Symbols In Python}. +@end defun + +@defun PendingFrame.find_sal () +Return the pending frame's symtab and line object (@pxref{Symbol +Tables In Python}). +@end defun + +@defun PendingFrame.language () +Return a string, the source language for this pending frame. +@end defun + @subheading Unwinder Output: UnwindInfo Use @code{PendingFrame.create_unwind_info} method described above to diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 12b14616363..5ed3ff37e64 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -28,6 +28,10 @@ #include "regcache.h" #include "valprint.h" #include "user-regs.h" +#include "stack.h" +#include "charset.h" +#include "block.h" + /* Debugging of Python unwinders. */ @@ -412,6 +416,200 @@ pending_framepy_read_register (PyObject *self, PyObject *args) return result; } +/* Implement PendingFrame.is_valid(). Return True if this pending frame + object is still valid. */ + +static PyObject * +pending_framepy_is_valid (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + if (pending_frame->frame_info == nullptr) + Py_RETURN_FALSE; + + Py_RETURN_TRUE; +} + +/* Implement PendingFrame.name(). Return a string that is the name of the + function for this frame, or None if the name can't be found. */ + +static PyObject * +pending_framepy_name (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + gdb::unique_xmalloc_ptr name; + + try + { + enum language lang; + frame_info_ptr frame = pending_frame->frame_info; + + name = find_frame_funname (frame, &lang, nullptr); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + if (name != nullptr) + return PyUnicode_Decode (name.get (), strlen (name.get ()), + host_charset (), nullptr); + + Py_RETURN_NONE; +} + +/* Implement gdb.PendingFrame.pc(). Returns an integer containing the + frame's current $pc value. */ + +static PyObject * +pending_framepy_pc (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + CORE_ADDR pc = 0; + + try + { + pc = get_frame_pc (pending_frame->frame_info); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + return gdb_py_object_from_ulongest (pc).release (); +} + +/* Implement gdb.PendingFrame.language(). Return the name of the language + for this frame. */ + +static PyObject * +pending_framepy_language (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + try + { + frame_info_ptr fi = pending_frame->frame_info; + + enum language lang = get_frame_language (fi); + const language_defn *lang_def = language_def (lang); + + return host_string_to_python_string (lang_def->name ()).release (); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + Py_RETURN_NONE; +} + +/* Implement PendingFrame.find_sal(). Return the PendingFrame's symtab and + line. */ + +static PyObject * +pending_framepy_find_sal (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + PyObject *sal_obj = nullptr; + + try + { + frame_info_ptr frame = pending_frame->frame_info; + + symtab_and_line sal = find_frame_sal (frame); + sal_obj = symtab_and_line_to_sal_object (sal); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + return sal_obj; +} + +/* Implement PendingFrame.block(). Return a gdb.Block for the pending + frame's code, or raise RuntimeError if the block can't be found. */ + +static PyObject * +pending_framepy_block (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + frame_info_ptr frame = pending_frame->frame_info; + const struct block *block = nullptr, *fn_block; + + try + { + block = get_frame_block (frame, nullptr); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + for (fn_block = block; + fn_block != nullptr && fn_block->function () == nullptr; + fn_block = fn_block->superblock ()) + ; + + if (block == nullptr + || fn_block == nullptr + || fn_block->function () == nullptr) + { + PyErr_SetString (PyExc_RuntimeError, + _("Cannot locate block for frame.")); + return nullptr; + } + + return block_to_block_object (block, fn_block->function ()->objfile ()); +} + +/* Implement gdb.PendingFrame.function(). Return a gdb.Symbol + representing the function of this frame, or None if no suitable symbol + can be found. */ + +static PyObject * +pending_framepy_function (PyObject *self, PyObject *args) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + + PENDING_FRAMEPY_REQUIRE_VALID (pending_frame); + + struct symbol *sym = nullptr; + + try + { + enum language funlang; + frame_info_ptr frame = pending_frame->frame_info; + + gdb::unique_xmalloc_ptr funname + = find_frame_funname (frame, &funlang, &sym); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + if (sym != nullptr) + return symbol_to_symbol_object (sym); + + Py_RETURN_NONE; +} + /* Implementation of PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */ @@ -737,6 +935,29 @@ static PyMethodDef pending_frame_object_methods[] = pending_framepy_architecture, METH_NOARGS, "architecture () -> gdb.Architecture\n" "The architecture for this PendingFrame." }, + { "name", + pending_framepy_name, METH_NOARGS, + "name() -> String.\n\ +Return the function name of the frame, or None if it can't be determined." }, + { "is_valid", + pending_framepy_is_valid, METH_NOARGS, + "is_valid () -> Boolean.\n\ +Return true if this PendingFrame is valid, false if not." }, + { "pc", + pending_framepy_pc, METH_NOARGS, + "pc () -> Long.\n\ +Return the frame's resume address." }, + { "language", pending_framepy_language, METH_NOARGS, + "The language of this frame." }, + { "find_sal", pending_framepy_find_sal, METH_NOARGS, + "find_sal () -> gdb.Symtab_and_line.\n\ +Return the frame's symtab and line." }, + { "block", pending_framepy_block, METH_NOARGS, + "block () -> gdb.Block.\n\ +Return the frame's code block." }, + { "function", pending_framepy_function, METH_NOARGS, + "function () -> gdb.Symbol.\n\ +Returns the symbol for the function corresponding to this frame." }, { "level", pending_framepy_level, METH_NOARGS, "The stack level of this frame." }, {NULL} /* Sentinel */ diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index 3e214ee0f45..f670da5a7cd 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -149,15 +149,46 @@ gdb_test_no_output "python obj = simple_unwinder(\"simple\")" gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj)" check_for_broken_backtrace "backtrace to capture a PendingFrame object" +# Check the captured PendingFrame is not valid. +gdb_test "python print(captured_pending_frame.is_valid())" "False" + # Call methods on the captured gdb.PendingFrame and check we see the # expected error. gdb_test_no_output "python pf = captured_pending_frame" foreach cmd {"pf.read_register(\"pc\")" \ "pf.create_unwind_info(None)" \ "pf.architecture()" \ - "pf.level()"} { + "pf.level()" \ + "pf.name()" \ + "pf.pc()" \ + "pf.language()" \ + "pf.find_sal()" \ + "pf.block()" \ + "pf.function()" } { gdb_test "python $cmd" \ [multi_line \ "ValueError: gdb\\.PendingFrame is invalid\\." \ "Error while executing Python code\\."] } + +# Turn on the useful unwinder so we have the full backtrace again, and +# disable the simple unwinder -- because we can! +gdb_test "enable unwinder global \"test unwinder\"" \ + "1 unwinder enabled" \ + "re-enable 'test unwinder' so we can check PendingFrame methods" +gdb_test "disable unwinder global \"simple\"" \ + "1 unwinder disabled" +check_for_fixed_backtrace \ + "check backtrace before testing PendingFrame methods" + +# Gather information about every frame. +gdb_test_no_output "python capture_all_frame_information()" +gdb_test_no_output "python gdb.newest_frame().select()" +gdb_test_no_output "python pspace = gdb.selected_inferior().progspace" +gdb_test_no_output "python obj = validating_unwinder()" +gdb_test_no_output "python gdb.unwinder.register_unwinder(pspace, obj)" + +check_for_fixed_backtrace \ + "check backtrace to validate all information" + +gdb_test_no_output "python check_all_frame_information_matched()" diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index b30e843e7e5..8e3c1f398bc 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -144,9 +144,113 @@ class simple_unwinder(Unwinder): def __call__(self, pending_frame): global captured_pending_frame + assert pending_frame.is_valid() + if captured_pending_frame is None: captured_pending_frame = pending_frame return None +# Return a dictionary of information about FRAME. +def capture_frame_information(frame): + name = frame.name() + level = frame.level() + language = frame.language() + function = frame.function() + architecture = frame.architecture() + pc = frame.pc() + sal = frame.find_sal() + try: + block = frame.block() + assert isinstance(block, gdb.Block) + except RuntimeError as rte: + assert str(rte) == "Cannot locate block for frame." + block = "RuntimeError: " + str(rte) + + return { + "name": name, + "level": level, + "language": language, + "function": function, + "architecture": architecture, + "pc": pc, + "sal": sal, + "block": block, + } + + +# List of information about each frame. The index into this list is +# the frame level. This is populated by +# capture_all_frame_information. +all_frame_information = [] + +# Fill in the global ALL_FRAME_INFORMATION list. +def capture_all_frame_information(): + global all_frame_information + + all_frame_information = [] + + gdb.newest_frame().select() + frame = gdb.selected_frame() + count = 0 + + while frame is not None: + frame.select() + info = capture_frame_information(frame) + level = info["level"] + info["matched"] = False + + while len(all_frame_information) <= level: + all_frame_information.append(None) + + assert all_frame_information[level] is None + all_frame_information[level] = info + + if frame.name == "main" or count > 10: + break + + count += 1 + frame = frame.older() + + +# Assert that every entry in the global ALL_FRAME_INFORMATION list was +# matched by the validating_unwinder. +def check_all_frame_information_matched(): + global all_frame_information + for entry in all_frame_information: + assert entry["matched"] + + +# An unwinder that doesn't match any frames. What it does do is +# lookup information from the PendingFrame object and compare it +# against information stored in the global ALL_FRAME_INFORMATION list. +class validating_unwinder(Unwinder): + def __init__(self): + super().__init__("validating_unwinder") + + def __call__(self, pending_frame): + info = capture_frame_information(pending_frame) + level = info["level"] + + global all_frame_information + old_info = all_frame_information[level] + + assert old_info is not None + assert not old_info["matched"] + + for key, value in info.items(): + assert key in old_info, f"{key} not in old_info" + assert type(value) == type(old_info[key]) + if isinstance(value, gdb.Block): + assert value.start == old_info[key].start + assert value.end == old_info[key].end + assert value.is_static == old_info[key].is_static + assert value.is_global == old_info[key].is_global + else: + assert str(value) == str(old_info[key]) + + old_info["matched"] = True + return None + + print("Python script imported") From patchwork Fri Mar 10 14:55:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66218 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8A1DE3857838 for ; Fri, 10 Mar 2023 14:56:55 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 8A1DE3857838 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460215; bh=TWrN7LJLgeufwKNewsReFmDEJCfjWZBLG6O+hIvM0n0=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=fTVHePdUwRC2VN5IsAaaYDSwNXgDsARtED5aIRiHLHL9kIlRjD1xm9vBiPap/gLvx 8gt/ggaQlWDhSjy+frX5nUdHFuPvO1/D9uCfzxgoFx7sexa8gbJNv+anJKj63vHIx/ L0DCFrni3mSIDW0BliBaMfhMZuqvvIRBus27D6Ds= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 23FC83858C1F for ; Fri, 10 Mar 2023 14:56:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 23FC83858C1F Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-341-hkXyAFhuMqi75n3dzgqC7Q-1; Fri, 10 Mar 2023 09:55:59 -0500 X-MC-Unique: hkXyAFhuMqi75n3dzgqC7Q-1 Received: by mail-wm1-f72.google.com with SMTP id c7-20020a7bc847000000b003e00be23a70so3975861wml.2 for ; Fri, 10 Mar 2023 06:55:59 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460158; 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=TWrN7LJLgeufwKNewsReFmDEJCfjWZBLG6O+hIvM0n0=; b=jXFwXQx4wPTqWfXxPUR1BWHb+OGw7d5UnXFZ3qD1ASMUer1e6X8NrJdKNnRDFcz/Ae b9RZVnDYOrAQzY4R6Tgv5FO7BoOE5PHfem9P8Om4AmEU/pF/yrhtEgJdUQ69Tf+T+CCH ZeQSQ6vA+oBtIpZoZr0lq5YU6ufTvmwhCQd1EQKv/Ekr7/g9UG/3wZlj72yytsXkpTtl bIisoQeJloE4J0aw2vU23JKzpX8h4QZePcjXsM2ncrR8ZX4qRez1EjkjWIjbsbvQzBaa 47x/r0MpfLdgHddT48XlKDW3AncUUUWHhWNEKRCKHvOi9unN4ypk/wfsblbx+zqBblqB jNCg== X-Gm-Message-State: AO0yUKUSG/CDKe/mWu87FO/5FtGvxG/SKTiTD38pDWpDVRo9XY/rL3NA l5qysjbpbNrgXBCa6A5zNV1zPskky5pWjqOmqqstd3T8n4VFpqGKG6EwTZ2hcik1j6KK2xM7qBA XD1hhSC2t/yvmVmIeCHe44GCIjruUsqLdYkzK52YS3jsFAw3CFOPeDugGBeSFMAe/Hc2uqE4M98 DQeL6URA== X-Received: by 2002:a5d:670f:0:b0:2ca:a36e:3441 with SMTP id o15-20020a5d670f000000b002caa36e3441mr17049913wru.15.1678460158213; Fri, 10 Mar 2023 06:55:58 -0800 (PST) X-Google-Smtp-Source: AK7set9bb5ASKz2NuWSuDl7NPJY0ej9QP5+g65ZCMLF/6mLNgOPvOYX/2TUiLgKrfeCIdFdoNROZMw== X-Received: by 2002:a5d:670f:0:b0:2ca:a36e:3441 with SMTP id o15-20020a5d670f000000b002caa36e3441mr17049902wru.15.1678460157878; Fri, 10 Mar 2023 06:55:57 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id c12-20020a5d4ccc000000b002c4084d3472sm7867wrt.58.2023.03.10.06.55.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:57 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 06/10] gdb/python: add __repr__ for PendingFrame and UnwindInfo Date: Fri, 10 Mar 2023 14:55:23 +0000 Message-Id: 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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" Having a useful __repr__ method can make debugging Python code that little bit easier. This commit adds __repr__ for gdb.PendingFrame and gdb.UnwindInfo classes, along with some tests. --- gdb/python/py-unwind.c | 67 +++++++++++++++++++++++++- gdb/testsuite/gdb.python/py-unwind.exp | 19 ++++++++ gdb/testsuite/gdb.python/py-unwind.py | 16 +++++- 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 5ed3ff37e64..1e94c243163 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -229,6 +229,38 @@ unwind_infopy_str (PyObject *self) return PyUnicode_FromString (stb.c_str ()); } +/* Implement UnwindInfo.__repr__(). */ + +static PyObject * +unwind_infopy_repr (PyObject *self) +{ + unwind_info_object *unwind_info = (unwind_info_object *) self; + pending_frame_object *pending_frame + = (pending_frame_object *) (unwind_info->pending_frame); + frame_info_ptr frame = pending_frame->frame_info; + + if (frame == nullptr) + return PyUnicode_FromFormat ("<%s for an invalid frame>", + Py_TYPE (self)->tp_name); + + std::string saved_reg_names; + struct gdbarch *gdbarch = pending_frame->gdbarch; + + for (const saved_reg ® : *unwind_info->saved_regs) + { + const char *name = gdbarch_register_name (gdbarch, reg.number); + if (saved_reg_names.empty ()) + saved_reg_names = name; + else + saved_reg_names = (saved_reg_names + ", ") + name; + } + + return PyUnicode_FromFormat ("<%s frame #%d, saved_regs=(%s)>", + Py_TYPE (self)->tp_name, + frame_relative_level (frame), + saved_reg_names.c_str ()); +} + /* Create UnwindInfo instance for given PendingFrame and frame ID. Sets Python error and returns NULL on error. @@ -372,6 +404,37 @@ pending_framepy_str (PyObject *self) return PyUnicode_FromFormat ("SP=%s,PC=%s", sp_str, pc_str); } +/* Implement PendingFrame.__repr__(). */ + +static PyObject * +pending_framepy_repr (PyObject *self) +{ + pending_frame_object *pending_frame = (pending_frame_object *) self; + frame_info_ptr frame = pending_frame->frame_info; + + if (frame == nullptr) + return PyUnicode_FromFormat ("<%s (invalid)>", Py_TYPE (self)->tp_name); + + const char *sp_str = nullptr; + const char *pc_str = nullptr; + + try + { + sp_str = core_addr_to_string_nz (get_frame_sp (frame)); + pc_str = core_addr_to_string_nz (get_frame_pc (frame)); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + return PyUnicode_FromFormat ("<%s level=%d, sp=%s, pc=%s>", + Py_TYPE (self)->tp_name, + frame_relative_level (frame), + sp_str, + pc_str); +} + /* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value. Returns the value of register REG as gdb.Value instance. */ @@ -974,7 +1037,7 @@ PyTypeObject pending_frame_object_type = 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + pending_framepy_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ @@ -1024,7 +1087,7 @@ PyTypeObject unwind_info_object_type = 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ - 0, /* tp_repr */ + unwind_infopy_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index f670da5a7cd..7e6ac9a8623 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -152,6 +152,25 @@ check_for_broken_backtrace "backtrace to capture a PendingFrame object" # Check the captured PendingFrame is not valid. gdb_test "python print(captured_pending_frame.is_valid())" "False" +# Check the __repr__ of an invalid PendingFrame. +gdb_test "python print(repr(captured_pending_frame))" \ + "" + +# Check the __repr__ of an UnwindInfo for an invalid PendingFrame. +gdb_test "python print(captured_unwind_info)" +gdb_test "python print(repr(captured_unwind_info))" \ + "" + +# Check the repr of a PendingFrame that was copied (as a string) at a +# time the PendingFrame was valid. +gdb_test "python print(captured_pending_frame_repr)" \ + "" + +# Check the repr of an UnwindInfo that was copied (as a string) at a +# time the UnwindInfo was valid. +gdb_test "python print(captured_unwind_info_repr)" \ + "" + # Call methods on the captured gdb.PendingFrame and check we see the # expected error. gdb_test_no_output "python pf = captured_pending_frame" diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index 8e3c1f398bc..f8f04b7f514 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -133,8 +133,11 @@ class TestUnwinder(Unwinder): global_test_unwinder = TestUnwinder() gdb.unwinder.register_unwinder(None, global_test_unwinder, True) -# This is filled in by the simple_unwinder class. +# These are filled in by the simple_unwinder class. captured_pending_frame = None +captured_pending_frame_repr = None +captured_unwind_info = None +captured_unwind_info_repr = None class simple_unwinder(Unwinder): @@ -143,11 +146,22 @@ class simple_unwinder(Unwinder): def __call__(self, pending_frame): global captured_pending_frame + global captured_pending_frame_repr + global captured_unwind_info + global captured_unwind_info_repr assert pending_frame.is_valid() if captured_pending_frame is None: captured_pending_frame = pending_frame + captured_pending_frame_repr = repr(pending_frame) + fid = FrameId(gdb.Value(0x123), gdb.Value(0x456)) + uw = pending_frame.create_unwind_info(fid) + uw.add_saved_register("rip", gdb.Value(0x123)) + uw.add_saved_register("rbp", gdb.Value(0x456)) + uw.add_saved_register("rsp", gdb.Value(0x789)) + captured_unwind_info = uw + captured_unwind_info_repr = repr(uw) return None From patchwork Fri Mar 10 14:55:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66224 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id ED5F538515F4 for ; Fri, 10 Mar 2023 14:58:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org ED5F538515F4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460290; bh=AgSoaOHdK7Itr6ZxIkg7AaDqP96IsuUjJQr++YE70co=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=Nj6qtkLo7EeJQtaplYoZnK/rmFsZ59XRCcpKH+ehYDM1Pk7AffXUig1WK+sXKjziA QLUXUPW2mIFPGirb6B4v79e0fCTx5czmuijq+dMBk+5ZNKFZXOk7ie1AjimRsNAhrw xKRsidnlIUKFSFSq3TZ3I0J7+lr0t6YN3PmpRTCw= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 49E403858298 for ; Fri, 10 Mar 2023 14:56:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 49E403858298 Received: from mail-wm1-f72.google.com (mail-wm1-f72.google.com [209.85.128.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-608-nUtdqXARP5-lKZi5GJ4Dhg-1; Fri, 10 Mar 2023 09:56:00 -0500 X-MC-Unique: nUtdqXARP5-lKZi5GJ4Dhg-1 Received: by mail-wm1-f72.google.com with SMTP id l31-20020a05600c1d1f00b003e8626cdd42so1917137wms.3 for ; Fri, 10 Mar 2023 06:56:00 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460159; 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=AgSoaOHdK7Itr6ZxIkg7AaDqP96IsuUjJQr++YE70co=; b=2TICRRMCGC4TYHTRBJe2PW84Y56l5UGSviQRhkaevVJ+co7E/hj9Mlg4L+4h9d7Mc1 QCv16/IM8AKc0z4Ult7gOERseouZoKRR+wCAOGRHvsxys7U5Fom1S+Gc1j40sN8I62ds 4ZlT7h/y37MxFFoYhaFZj7iWZrv7t+z64zGljPafyb1M+zqFqHyIBirlA+NSB47eu8Xb f884lnNwJGkMCO7122xJT2jZWG5cTEdTHn8U8z8v5xmHgdYtp7NEWXRNpu974yr54w5K PMU3DQwpxY0XClUCabfLpFJGrye/5qT8fIcBolYt7FxWWGpgvqtId9w+IEmgc953Gj1z 7Igw== X-Gm-Message-State: AO0yUKWZdYxKJObUQxYqF2XHQi/gKpAJLJKdn93fJWxUI12Mr+3VzbU+ 5p34sh8ZmlL+ylvoHiEVd/+80i9Cn7YJnh0rf3uBDZIQ+Xy8rJKJMnIjyx7yNOSbHVXdgZuJIMF FcJ4lTOBpDzDTYUT3y8L2PG5YAJ/uiGhnl0veCpGsMiGQn+6X7K+tOSlAd3QZCW5DDOOipefmWj Qo+iJbew== X-Received: by 2002:adf:efca:0:b0:2ce:34c8:44c with SMTP id i10-20020adfefca000000b002ce34c8044cmr16718186wrp.19.1678460159579; Fri, 10 Mar 2023 06:55:59 -0800 (PST) X-Google-Smtp-Source: AK7set9my3stKZbnnN27Bk4KWScNLcb/ikJkFQDnn88u5WvsOZv/CGGvWfZ1kp2StrVe9CBSZq9gtg== X-Received: by 2002:adf:efca:0:b0:2ce:34c8:44c with SMTP id i10-20020adfefca000000b002ce34c8044cmr16718172wrp.19.1678460159157; Fri, 10 Mar 2023 06:55:59 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id n6-20020a5d6b86000000b002c54c8e70b1sm31791wrx.9.2023.03.10.06.55.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:55:58 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 07/10] gdb/python: remove Py_TPFLAGS_BASETYPE from gdb.UnwindInfo Date: Fri, 10 Mar 2023 14:55:24 +0000 Message-Id: <14071218f24a990e20eab6d0fb427594be1baf05.1678460067.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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" It is not currently possible to directly create gdb.UnwindInfo instances, they need to be created by calling gdb.PendingFrame.create_unwind_info so that the newly created UnwindInfo can be linked to the pending frame. As such there's no tp_init method defined for UnwindInfo. A consequence of all this is that it doesn't really make sense to allow sub-classing of gdb.UnwindInfo. Any sub-class can't call the parents __init__ method to correctly link up the PendingFrame object (there is no parent __init__ method). And any instances that sub-classes UnwindInfo but doesn't call the parent __init__ is going to be invalid for use in GDB. This commit removes the Py_TPFLAGS_BASETYPE flag from the UnwindInfo class, which prevents the class being sub-classed. Then I've added a test to check that this is indeed prevented. Any functional user code will not have any issues with this change. --- gdb/python/py-unwind.c | 2 +- gdb/testsuite/gdb.python/py-unwind.exp | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 1e94c243163..432a26a760a 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -1097,7 +1097,7 @@ PyTypeObject unwind_info_object_type = 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ "GDB UnwindInfo object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index 7e6ac9a8623..d65b8447525 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -211,3 +211,20 @@ check_for_fixed_backtrace \ "check backtrace to validate all information" gdb_test_no_output "python check_all_frame_information_matched()" + +# Check we can't sub-class from gdb.UnwindInfo. +gdb_test_multiline "Sub-class gdb.UnwindInfo " \ + "python" "" \ + "class my_unwind_info(gdb.UnwindInfo):" "" \ + " def __init__(self):" "" \ + " pass" "" \ + "end" \ + [multi_line \ + "TypeError: type 'gdb\\.UnwindInfo' is not an acceptable base type" \ + "Error while executing Python code\\."] + +# Check we can't directly instantiate a gdb.UnwindInfo. +gdb_test "python uw = gdb.UnwindInfo()" \ + [multi_line \ + "TypeError: cannot create 'gdb\\.UnwindInfo' instances" \ + "Error while executing Python code\\."] From patchwork Fri Mar 10 14:55:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66220 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 0A978385559A for ; Fri, 10 Mar 2023 14:57:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 0A978385559A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460239; bh=pZrFZLWSxRZq6ZOmgxqHbFUOyxGfuKCFCgSPqnEcbLI=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=mMExT4df9i8c3H6UJ0GJ+xOhMF37OTfO7rSpkNs9+GSQRGNl37pjHDZNW0b5JMQqk 4ezU7ChAxIv+y5amPgz2tZiRHuYImPaAPHJC9ctxJrbOB14WsPbOuIv7rBtLPQGTgk 9ilQRFR9AhrNIo1SsufmlSTn8ZmcdKep1SDKTXGc= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 65BF1385840D for ; Fri, 10 Mar 2023 14:56:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 65BF1385840D Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-492-scvPblTcMimFmT1dkvoQtw-1; Fri, 10 Mar 2023 09:56:01 -0500 X-MC-Unique: scvPblTcMimFmT1dkvoQtw-1 Received: by mail-wr1-f72.google.com with SMTP id a7-20020a056000188700b002cdd0562b11so1119280wri.11 for ; Fri, 10 Mar 2023 06:56:01 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460160; 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=pZrFZLWSxRZq6ZOmgxqHbFUOyxGfuKCFCgSPqnEcbLI=; b=yAZHE9G7cg3j4RPKXHe/Jm9/tsIk4Dx5qL2BH+B4oyni8uEXfyszSsAUH/NS+bj768 x4xLOa3e9r3aN0YV6Cz5oqrdbFc0vjetv55uvPlhNFFNsoIQSjqj+ZcRzG8SuOth4jGA W+lom5TdyDBo9I0crzfNGu3jDvN7IP0lSjKoQ1lmxhQwtPSHPaKFlGfbFA0tyr0u4u/k IJp3/xiQ9ODP7FKWAYOdc8iXpHC04hSRy3kksH4R/zX/Mo7L2Bg4NQllqvdHcXAA6w2K fuSn2mAnYRj6OS/gRNSG864KxXZl8oAUr6114i8JaAVJR2XL2VWTPH1DcJIRCYJpMXOL 2pBQ== X-Gm-Message-State: AO0yUKVz1WeQIJLVbW6fx2GuG9ZACnZumPebltWTmPcC9bgazCqear4b ZfYaLz1xzZGDoa2KAaKRWl/1xtlyq6rE683c3L9xP2lS5keHMyhX2M/1IJJ03AQmWGSqMN7ZPmc hzbsv6lwaJ2Jor96pHEpNtUaM+/+5+aU9oxEbQ/ji0wIFmSfhgfSNz8dFtVMNYt/8pz5fJ9cnkz 2fRof9oQ== X-Received: by 2002:a5d:4751:0:b0:2c5:76bd:c0f3 with SMTP id o17-20020a5d4751000000b002c576bdc0f3mr17963568wrs.6.1678460160648; Fri, 10 Mar 2023 06:56:00 -0800 (PST) X-Google-Smtp-Source: AK7set83mSL4HyxYhG+pCrvcbbi31Ddz65pPENMFWjL6v82HxHtt3I7GPH0z5NmAWXy0lMxov6yKhw== X-Received: by 2002:a5d:4751:0:b0:2c5:76bd:c0f3 with SMTP id o17-20020a5d4751000000b002c576bdc0f3mr17963553wrs.6.1678460160302; Fri, 10 Mar 2023 06:56:00 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id c16-20020adffb50000000b002c5544b3a69sm2365922wrs.89.2023.03.10.06.55.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:56:00 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 08/10] gdb: have value_as_address call unpack_pointer Date: Fri, 10 Mar 2023 14:55:25 +0000 Message-Id: <312bba523f1d5701df37f3c7984c1b18fc4b5f9d.1678460067.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 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" While refactoring some other code in gdb/python/* I wanted to merge two code paths. One path calls value_as_address, while the other calls unpack_pointer. I suspect calling value_as_address is the correct choice, but, while examining the code I noticed that value_as_address calls unpack_long rather than unpack_pointer. Under the hood, unpack_pointer does just call unpack_long so there's no real difference here, but it feels like value_as_address should call unpack_pointer. I've updated the code to use unpack_pointer, and changed a related comment to say that we call unpack_pointer. I've also adjusted the header comment on value_as_address. The existing header refers to some code that is now commented out. Rather than trying to describe the whole algorithm of value_as_address, which is already well commented within the function, I've just trimmed the comment on value_as_address to be a brief summary of what the function does. There should be no user visible changes after this commit. --- gdb/value.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/gdb/value.c b/gdb/value.c index 7b4df338304..d432b29b61b 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -2551,9 +2551,8 @@ value_as_long (struct value *val) return unpack_long (val->type (), val->contents ().data ()); } -/* Extract a value as a C pointer. Does not deallocate the value. - Note that val's type may not actually be a pointer; value_as_long - handles all the cases. */ +/* Extract a value as a C pointer. Does not deallocate the value. */ + CORE_ADDR value_as_address (struct value *val) { @@ -2592,7 +2591,7 @@ value_as_address (struct value *val) to COERCE_ARRAY below actually does all the usual unary conversions, which includes converting values of type `function' to `pointer to function'. This is the challenging conversion - discussed above. Then, `unpack_long' will convert that pointer + discussed above. Then, `unpack_pointer' will convert that pointer back into an address. So, suppose the user types `disassemble foo' on an architecture @@ -2653,7 +2652,7 @@ value_as_address (struct value *val) return gdbarch_integer_to_address (gdbarch, val->type (), val->contents ().data ()); - return unpack_long (val->type (), val->contents ().data ()); + return unpack_pointer (val->type (), val->contents ().data ()); #endif } From patchwork Fri Mar 10 14:55:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66223 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3568B384F01A for ; Fri, 10 Mar 2023 14:57:47 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3568B384F01A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460267; bh=xs2IAq7wpz2A7jTeBoMWo/+TZScZ3AaLR+NAb5DPP2c=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=wTm8dXzjDqMxepQL+4zpdSPE0Hc7dV6hsFWxEFpFcNP3dVbFKlIKHL8pOa2JtYho2 2m4pfk62Ya9D+DOqttN6PbnIFMM6q5zqwbUpQA5KAswxGQR6qDGrgWXR99hPl4CGA3 couP4fhDZkXbALcphdlGI3C9EFbxn8zehDVIlC3U= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id A095F385800A for ; Fri, 10 Mar 2023 14:56:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A095F385800A Received: from mail-wm1-f71.google.com (mail-wm1-f71.google.com [209.85.128.71]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-41-n5fePA-vNEyOFtwAinAa6w-1; Fri, 10 Mar 2023 09:56:03 -0500 X-MC-Unique: n5fePA-vNEyOFtwAinAa6w-1 Received: by mail-wm1-f71.google.com with SMTP id e17-20020a05600c219100b003e21fa60ec1so2076485wme.2 for ; Fri, 10 Mar 2023 06:56:03 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460162; 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=xs2IAq7wpz2A7jTeBoMWo/+TZScZ3AaLR+NAb5DPP2c=; b=dl16E9U4NOCrtogMi1rmpup7paEd70yPIOF4t0d9zKfLTLuZtG26j8Ax2b0pubw5dm X6OIRZbTxwCE8cAV/RnwQ5Z1L+Oahpwna4UQ74zD7enl+yygUbymEtqXkJb+bCo4GvW/ F8DHbbZ1q8X/Ynrtpsp3/OyeJdsnAecwXt9e6ADY8mTuXtFHBElk69EhAhY8xqXq12/0 oAAK22+dpR0VERxE7xZ6NreW36M1stkWSWdxXYFgvPgHkX2LMghLbmGvxv6ubIZFNN8A trr+Ll7gPgS6M0iNnPlLnPkn1TxFY2y75UW4SYblzWmmxFStDMlqdQ+BSh8qukE5/xDe /3kA== X-Gm-Message-State: AO0yUKXYDbJUIlxlKGvyHDY2pXB2Le21wcwVXitZX1sZmsYy/JB3mBFp MFsdRF+npsIsVp13ZHWnaLd5Rx6n1Rf/908vrNsIcQV5DGMIofwZ83TvgY38AEGTckZfGN58Vbj ytxrFwLBOJStul2TUqeIFQ72RoA+svG9vW/wEUG/kR1q5BBxiUZWjaJ5fyb+WKkCiRxvoxSjq4c +gjDoiSA== X-Received: by 2002:a05:600c:3106:b0:3dc:18de:b20d with SMTP id g6-20020a05600c310600b003dc18deb20dmr3129716wmo.33.1678460161971; Fri, 10 Mar 2023 06:56:01 -0800 (PST) X-Google-Smtp-Source: AK7set/KiLGMPpEMs11D32WV2PWEuy/S3T0eOjP40Hy5VwpELinHOMINc3QYuoSHZkoKltcqp6Tjqg== X-Received: by 2002:a05:600c:3106:b0:3dc:18de:b20d with SMTP id g6-20020a05600c310600b003dc18deb20dmr3129698wmo.33.1678460161536; Fri, 10 Mar 2023 06:56:01 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id j26-20020a05600c1c1a00b003e8f0334db8sm189338wms.5.2023.03.10.06.56.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:56:01 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 09/10] gdb/python: Allow gdb.UnwindInfo to be created with non gdb.Value args Date: Fri, 10 Mar 2023 14:55:26 +0000 Message-Id: <9da6361cc56f184b6a44b1086d9fbeae6d30eab9.1678460067.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 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, 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" Currently when creating a gdb.UnwindInfo object a user must call gdb.PendingFrame.create_unwind_info and pass a frame-id object. The frame-id object should have at least a 'sp' attribute, and probably a 'pc' attribute too (it can also, in some cases have a 'special' attribute). Currently all of these frame-id attributes need to be gdb.Value objects, but the only reason for that requirement is that we have some code in py-unwind.c that only handles gdb.Value objects. If instead we switch to using get_addr_from_python in py-utils.c then we will support both gdb.Value objects and also raw numbers, which might make things simpler in some cases. So, I started rewriting pyuw_object_attribute_to_pointer (in py-unwind.c) to use get_addr_from_python. However, while looking at the code I noticed a problem. The pyuw_object_attribute_to_pointer function returns a boolean flag, if everything goes OK we return true, but we return false in two cases, (1) when the attribute is not present, which might be acceptable, or might be an error, and (2) when we get an error trying to extract the attribute value, in which case a Python error will have been set. Now in pending_framepy_create_unwind_info we have this code: if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp)) { PyErr_SetString (PyExc_ValueError, _("frame_id should have 'sp' attribute.")); return NULL; } Notice how we always set an error. This will override any error that is already set. So, if you create a frame-id object that has an 'sp' attribute, but the attribute is not a gdb.Value, then currently we fail to extract the attribute value (it's not a gdb.Value) and set this error in pyuw_object_attribute_to_pointer: rc = pyuw_value_obj_to_pointer (pyo_value.get (), addr); if (!rc) PyErr_Format ( PyExc_ValueError, _("The value of the '%s' attribute is not a pointer."), attr_name); Then we return to pending_framepy_create_unwind_info and immediately override this error with the error about 'sp' being missing. This all feels very confused. Here's my proposed solution: pyuw_object_attribute_to_pointer will now return a tri-state enum, with states OK, MISSING, or ERROR. The meanings of these states are: OK - Attribute exists and was extracted fine, MISSING - Attribute doesn't exist, no Python error was set. ERROR - Attribute does exist, but there was an error while extracting it, a Python error was set. We need to update pending_framepy_create_unwind_info, the only user of pyuw_object_attribute_to_pointer, but now I think things are much clearer. Errors from lower levels are not blindly overridden with the generic meaningless error message, but we still get the "missing 'sp' attribute" error when appropriate. This change also includes the switch to get_addr_from_python which was what started this whole journey. For well behaving user code there should be no visible changes after this commit. For user code that hits an error, hopefully the new errors should be more helpful in figuring out what's gone wrong. Additionally, users can now use integers for the 'sp' and 'pc' attributes in their frame-id objects if that is useful. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 4 + gdb/doc/python.texi | 3 +- gdb/python/py-unwind.c | 113 ++++++++++++++----------- gdb/testsuite/gdb.python/py-unwind.exp | 36 ++++++++ gdb/testsuite/gdb.python/py-unwind.py | 6 +- 5 files changed, 111 insertions(+), 51 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index 0d9049ff134..c4f7de11c6e 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -130,6 +130,10 @@ show always-read-ctf - gdb.PendingFrame.function(): Return a gdb.Symbol for the current pending frame, or None. + ** The frame-id passed to gdb.PendingFrame.create_unwind_info can + now use either an integer or a gdb.Value object for each of its + 'sp', 'pc', and 'special' attributes. + *** Changes in GDB 13 * MI version 1 is deprecated, and will be removed in GDB 14. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 1c4239841af..3074b5250a3 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2815,7 +2815,8 @@ this. @end table -Each attribute value should be an instance of @code{gdb.Value}. +Each attribute value should either be an instance of @code{gdb.Value} +or an integer. @end defun diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index 432a26a760a..409dbd3a470 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -132,58 +132,63 @@ extern PyTypeObject pending_frame_object_type extern PyTypeObject unwind_info_object_type CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("unwind_info_object"); -/* Convert gdb.Value instance to inferior's pointer. Return 1 on success, - 0 on failure. */ +/* An enum returned by pyuw_object_attribute_to_pointer, a function which + is used to extract an attribute from a Python object. */ -static int -pyuw_value_obj_to_pointer (PyObject *pyo_value, CORE_ADDR *addr) +enum class pyuw_get_attr_code { - int rc = 0; - struct value *value; + /* The attribute was present, and its value was successfully extracted. */ + ATTR_OK, - try - { - if ((value = value_object_to_value (pyo_value)) != NULL) - { - *addr = unpack_pointer (value->type (), - value->contents ().data ()); - rc = 1; - } - } - catch (const gdb_exception &except) - { - gdbpy_convert_exception (except); - } - return rc; -} + /* The attribute was not present, or was present and its value was None. + No Python error has been set. */ + ATTR_MISSING, -/* Get attribute from an object and convert it to the inferior's - pointer value. Return 1 if attribute exists and its value can be - converted. Otherwise, if attribute does not exist or its value is - None, return 0. In all other cases set Python error and return - 0. */ + /* The attribute was present, but there was some error while trying to + get the value from the attribute. A Python error will be set when + this is returned. */ + ATTR_ERROR, +}; -static int +/* Get the attribute named ATTR_NAME from the object PYO and convert it to + an inferior pointer value, placing the pointer in *ADDR. + + Return pyuw_get_attr_code::ATTR_OK if the attribute was present and its + value was successfully written into *ADDR. For any other return value + the contents of *ADDR are undefined. + + Return pyuw_get_attr_code::ATTR_MISSING if the attribute was not + present, or it was present but its value was None. The contents of + *ADDR are undefined in this case. No Python error will be set in this + case. + + Return pyuw_get_attr_code::ATTR_ERROR if the attribute was present, but + there was some error while extracting the attribute's value. A Python + error will be set in this case. The contents of *ADDR are undefined. */ + +static pyuw_get_attr_code pyuw_object_attribute_to_pointer (PyObject *pyo, const char *attr_name, CORE_ADDR *addr) { - int rc = 0; + if (!PyObject_HasAttrString (pyo, attr_name)) + return pyuw_get_attr_code::ATTR_MISSING; - if (PyObject_HasAttrString (pyo, attr_name)) + gdbpy_ref<> pyo_value (PyObject_GetAttrString (pyo, attr_name)); + if (pyo_value == nullptr) { - gdbpy_ref<> pyo_value (PyObject_GetAttrString (pyo, attr_name)); + gdb_assert (PyErr_Occurred ()); + return pyuw_get_attr_code::ATTR_ERROR; + } + if (pyo_value == Py_None) + return pyuw_get_attr_code::ATTR_MISSING; - if (pyo_value != NULL && pyo_value != Py_None) - { - rc = pyuw_value_obj_to_pointer (pyo_value.get (), addr); - if (!rc) - PyErr_Format ( - PyExc_ValueError, - _("The value of the '%s' attribute is not a pointer."), - attr_name); - } + if (get_addr_from_python (pyo_value.get (), addr) < 0) + { + gdb_assert (PyErr_Occurred ()); + return pyuw_get_attr_code::ATTR_ERROR; } - return rc; + + return pyuw_get_attr_code::ATTR_OK; } /* Called by the Python interpreter to obtain string representation @@ -687,13 +692,18 @@ pending_framepy_create_unwind_info (PyObject *self, PyObject *args) PENDING_FRAMEPY_REQUIRE_VALID ((pending_frame_object *) self); if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id)) - return NULL; - if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp)) + return nullptr; + + pyuw_get_attr_code code + = pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp); + if (code == pyuw_get_attr_code::ATTR_MISSING) { PyErr_SetString (PyExc_ValueError, _("frame_id should have 'sp' attribute.")); - return NULL; + return nullptr; } + else if (code == pyuw_get_attr_code::ATTR_ERROR) + return nullptr; /* The logic of building frame_id depending on the attributes of the frame_id object: @@ -704,13 +714,20 @@ pending_framepy_create_unwind_info (PyObject *self, PyObject *args) Y Y N frame_id_build (sp, pc) Y Y Y frame_id_build_special (sp, pc, special) */ - if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc)) + code = pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc); + if (code == pyuw_get_attr_code::ATTR_ERROR) + return nullptr; + else if (code == pyuw_get_attr_code::ATTR_MISSING) return pyuw_create_unwind_info (self, frame_id_build_wild (sp)); - if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special)) + + code = pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special); + if (code == pyuw_get_attr_code::ATTR_ERROR) + return nullptr; + else if (code == pyuw_get_attr_code::ATTR_MISSING) return pyuw_create_unwind_info (self, frame_id_build (sp, pc)); - else - return pyuw_create_unwind_info (self, - frame_id_build_special (sp, pc, special)); + + return pyuw_create_unwind_info (self, + frame_id_build_special (sp, pc, special)); } /* Implementation of PendingFrame.architecture (self) -> gdb.Architecture. */ diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index d65b8447525..fddf4f15393 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -200,6 +200,42 @@ gdb_test "disable unwinder global \"simple\"" \ check_for_fixed_backtrace \ "check backtrace before testing PendingFrame methods" +# Turn the 'simple' unwinder back on. +gdb_test "enable unwinder global \"simple\"" \ + "1 unwinder enabled" + +# Replace the "simple" unwinder with a new version that doesn't set +# the 'sp' attribute. Also the 'pc' attribute is invalid, but we'll +# hit the missing 'sp' error first. +with_test_prefix "frame-id 'sp' is None" { + gdb_test_no_output "python obj = simple_unwinder(\"simple\", None, \"xyz\")" + gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" + gdb_test_no_output "python captured_pending_frame = None" + gdb_test "backtrace" \ + "Python Exception : frame_id should have 'sp' attribute\\.\r\n.*" +} + +# Replace the "simple" unwinder with a new version that sets the 'sp' +# attribute to an invalid value. Also the 'pc' attribute is invalid, but we'll +# hit the invalid 'sp' error first. +with_test_prefix "frame-id 'sp' is invalid" { + gdb_test_no_output "python obj = simple_unwinder(\"simple\", \"jkl\", \"xyz\")" + gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" + gdb_test_no_output "python captured_pending_frame = None" + gdb_test "backtrace" \ + "Python Exception : invalid literal for int\\(\\) with base 10: 'jkl'\r\n.*" +} + +# Replace the "simple" unwinder with a new version that sets the 'sp' +# to a valid value, but set the 'pc' attribute to an invalid value. +with_test_prefix "frame-id 'pc' is invalid" { + gdb_test_no_output "python obj = simple_unwinder(\"simple\", 0x123, \"xyz\")" + gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" + gdb_test_no_output "python captured_pending_frame = None" + gdb_test "backtrace" \ + "Python Exception : invalid literal for int\\(\\) with base 10: 'xyz'\r\n.*" +} + # Gather information about every frame. gdb_test_no_output "python capture_all_frame_information()" gdb_test_no_output "python gdb.newest_frame().select()" diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index f8f04b7f514..dbabb006e4b 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -141,8 +141,10 @@ captured_unwind_info_repr = None class simple_unwinder(Unwinder): - def __init__(self, name): + def __init__(self, name, sp=0x123, pc=0x456): super().__init__(name) + self._sp = sp + self._pc = pc def __call__(self, pending_frame): global captured_pending_frame @@ -155,7 +157,7 @@ class simple_unwinder(Unwinder): if captured_pending_frame is None: captured_pending_frame = pending_frame captured_pending_frame_repr = repr(pending_frame) - fid = FrameId(gdb.Value(0x123), gdb.Value(0x456)) + fid = FrameId(self._sp, self._pc) uw = pending_frame.create_unwind_info(fid) uw.add_saved_register("rip", gdb.Value(0x123)) uw.add_saved_register("rbp", gdb.Value(0x456)) From patchwork Fri Mar 10 14:55:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andrew Burgess X-Patchwork-Id: 66221 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 53731385559A for ; Fri, 10 Mar 2023 14:57:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 53731385559A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1678460241; bh=8o4zp0EY7mnI95BmBWFvE2ddWETu3vN69sjbs++o6CU=; h=To:Cc:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=UxcPhFO+uykKVntMb5LuKat4s4CtPWaviMXKx0sCvVL3/pYMjCYt8U9HYUJvuLK24 JHKZm0NvaGRyV0DqsG61FFB/au4an3lwIGT837W7BlMy0W8rHLU8TAuscJT5vgqIVc 2+graZOUhhbZwrIgaf13EK1komcz+jXk/rAJMC2A= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org 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 E27AD3858C36 for ; Fri, 10 Mar 2023 14:56:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E27AD3858C36 Received: from mail-wm1-f69.google.com (mail-wm1-f69.google.com [209.85.128.69]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-670-QOPNght4N3CSVXQ3gTfwGQ-1; Fri, 10 Mar 2023 09:56:04 -0500 X-MC-Unique: QOPNght4N3CSVXQ3gTfwGQ-1 Received: by mail-wm1-f69.google.com with SMTP id k36-20020a05600c1ca400b003eac86e4387so3958979wms.8 for ; Fri, 10 Mar 2023 06:56:04 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1678460163; 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=8o4zp0EY7mnI95BmBWFvE2ddWETu3vN69sjbs++o6CU=; b=SwNjYFPc/lwI674uxptgYZDltUZPvrhAzy1tYqK+qTuzQOg99ddvbWi3//Q7/m5ssy oSEQHgdPEU/gp56DuhOJiQcJ4GsFD8ac7oX5ckfrqGf9T9eELjkhtzrBAFfjM6igrzmf DSvBjbfXp6tTcFMQiKlFN9xsoUshmbFc+TYj6kQKoV12aBlGOVmjvknCsuWgnplU3Y7l XGGCZATrdqcegeaQRZzVPodoBh8UjUn3buIqSuseWuDNZ2OZOJ6afBNVKtHIpizoL0DM yY6Khyd0in/oWnFQPvqGX1WDAx0YTlSeexI7AWml0n46l6Q3RKK6HMnfBHW2+jrxI+8i DI5w== X-Gm-Message-State: AO0yUKVtMncSQ4nU/KB2A6oe2pLz77UyqNK+WMrV5+GLaFPeuWfTVJwp 9ljcD8LvDpldeJ4Cm1Rr/1T2VGbqb4kWsukmho6p9B0xuCxdpY5mvMBB8PdkoTmJC3rFSE+Kaci mqVsDbJ5CTgtceV9dIcgjtYKDfRR7GtusrxqIkm4FzOQyAp88Z5e3YUJ20OFluCCxjfwSt+uQKC AWWnO3HQ== X-Received: by 2002:a05:600c:ccf:b0:3eb:36fa:b78d with SMTP id fk15-20020a05600c0ccf00b003eb36fab78dmr2935638wmb.23.1678460162910; Fri, 10 Mar 2023 06:56:02 -0800 (PST) X-Google-Smtp-Source: AK7set/01e+YmLWjw6gOy3ehJcNsVf37mw9xMSLJmH8X6nM3t7wDGWHNp89u31P6pNPAw3oE7jpoeg== X-Received: by 2002:a05:600c:ccf:b0:3eb:36fa:b78d with SMTP id fk15-20020a05600c0ccf00b003eb36fab78dmr2935625wmb.23.1678460162658; Fri, 10 Mar 2023 06:56:02 -0800 (PST) Received: from localhost (95.72.115.87.dyn.plus.net. [87.115.72.95]) by smtp.gmail.com with ESMTPSA id m8-20020a05600c4f4800b003db01178b62sm87504wmq.40.2023.03.10.06.56.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 10 Mar 2023 06:56:02 -0800 (PST) To: gdb-patches@sourceware.org Cc: Andrew Burgess Subject: [PATCH 10/10] gdb/python: Add new gdb.unwinder.FrameId class Date: Fri, 10 Mar 2023 14:55:27 +0000 Message-Id: <3f0b625f18141d66fe610cf06bc82cd644c50751.1678460067.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 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 X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Andrew Burgess via Gdb-patches From: Andrew Burgess Reply-To: Andrew Burgess Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" When writing an unwinder it is necessary to create a new class to act as a frame-id. This new class is almost certainly just going to set a 'sp' and 'pc' attribute within the instance. This commit adds a little helper class gdb.unwinder.FrameId that does this job. Users can make use of this to avoid having to write out standard boilerplate code any time they write an unwinder. Of course, if the user wants their FrameId class to be more complicated in some way, then they can still write their own class, just like they could before. I've simplified the example code in the documentation to now use the new helper class, and I've also made use of this helper within the testsuite. Any existing user code will continue to work just as it did before after this change. Reviewed-By: Eli Zaretskii --- gdb/NEWS | 5 +++ gdb/doc/python.texi | 49 ++++++++++++++++++++++----- gdb/python/lib/gdb/unwinder.py | 26 ++++++++++++++ gdb/testsuite/gdb.python/py-unwind.py | 16 +-------- 4 files changed, 73 insertions(+), 23 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index c4f7de11c6e..c5aa256c1bd 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -134,6 +134,11 @@ show always-read-ctf now use either an integer or a gdb.Value object for each of its 'sp', 'pc', and 'special' attributes. + ** A new class gdb.unwinder.FrameId has been added. Instrances of + this class are constructed with 'sp' (stack-pointer) and 'pc' + (program-counter) values, and can be used as the frame-id when + calling gdb.PendingFrame.create_unwind_info. + *** Changes in GDB 13 * MI version 1 is deprecated, and will be removed in GDB 14. diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 3074b5250a3..bbcaf3fd166 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2782,6 +2782,7 @@ It also provides a factory method to create a @code{gdb.UnwindInfo} instance to be returned to @value{GDBN}: +@anchor{gdb.PendingFrame.create_unwind_info} @defun PendingFrame.create_unwind_info (frame_id) Returns a new @code{gdb.UnwindInfo} instance identified by given @var{frame_id}. The @var{frame_id} is used internally by @value{GDBN} @@ -2818,6 +2819,10 @@ Each attribute value should either be an instance of @code{gdb.Value} or an integer. +A helper class is provided in the @code{gdb.unwinder} module that can +be used to represent a frame-id +(@pxref{gdb.unwinder.FrameId,,@code{gdb.unwinder.FrameId}}). + @end defun @defun PendingFrame.architecture () @@ -2886,7 +2891,7 @@ @subheading The @code{gdb.unwinder} Module @value{GDBN} comes with a @code{gdb.unwinder} module which contains -the following class: +the following classes: @deftp {class} gdb.unwinder.Unwinder The @code{Unwinder} class is a base class from which user created @@ -2910,6 +2915,40 @@ @end defvar @end deftp +@anchor{gdb.unwinder.FrameId} +@deftp {class} gdb.unwinder.FrameId +This is a class suitable for being used as the frame-id when calling +@code{gdb.PendingFrame.create_unwind_info}. It is not required to use +this class, any class with the required attribute +(@pxref{gdb.PendingFrame.create_unwind_info,,@code{gdb.PendingFrame.create_unwind_info}}) +will be accepted, but in most cases this class will be sufficient. + +@code{gdb.unwinder.FrameId} has the following method: + +@defun gdb.unwinder.FrameId.__init__(@var{sp}, @var{pc}, @var{special} = @code{None}) +The @var{sp} and @var{pc} arguments are required and should be either +a @code{gdb.Value} object, or an integer. + +The @var{special} argument is optional, if specified it should be a +@code{gdb.Value} object, or an integer. +@end defun + +@code{gdb.unwinder.FrameId} has the following read-only attributes: + +@defvar gdb.unwinder.sp +The @var{sp} value passed to the constructor. +@end defvar + +@defvar gdb.unwinder.pc +The @var{pc} value passed to the constructor. +@end defvar + +@defvar gdb.unwinder.special +The @var{special} value passed to the constructor, or @code{None} if +no such value was passed. +@end defvar +@end deftp + @subheading Registering an Unwinder Object files and program spaces can have unwinders registered with @@ -2943,13 +2982,7 @@ Here is an example of how and structure a user created unwinder: @smallexample -from gdb.unwinder import Unwinder - -class FrameId(object): - def __init__(self, sp, pc): - self.sp = sp - self.pc = pc - +from gdb.unwinder import Unwinder, FrameId class MyUnwinder(Unwinder): def __init__(self): diff --git a/gdb/python/lib/gdb/unwinder.py b/gdb/python/lib/gdb/unwinder.py index 1303245c054..140b84d3374 100644 --- a/gdb/python/lib/gdb/unwinder.py +++ b/gdb/python/lib/gdb/unwinder.py @@ -69,6 +69,32 @@ class Unwinder(object): raise NotImplementedError("Unwinder __call__.") +class FrameId(object): + """A Frame-ID class for use when creating gdb.UnwindInfo objects. + + Attributes (all read-only): + pc: Program counter value. + sp: The stack-pointer value. + special: An alternative stack-pointer value, can be None.""" + + def __init__(self, sp, pc, special=None): + self._sp = sp + self._pc = pc + self._special = special + + @property + def sp(self): + return self._sp + + @property + def pc(self): + return self._pc + + @property + def special(self): + return self._special + + def register_unwinder(locus, unwinder, replace=False): """Register unwinder in given locus. diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index dbabb006e4b..4e110c51e3b 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -14,7 +14,7 @@ # along with this program. If not, see . import gdb -from gdb.unwinder import Unwinder +from gdb.unwinder import Unwinder, FrameId # These are set to test whether invalid register names cause an error. @@ -22,20 +22,6 @@ add_saved_register_error = False read_register_error = False -class FrameId(object): - def __init__(self, sp, pc): - self._sp = sp - self._pc = pc - - @property - def sp(self): - return self._sp - - @property - def pc(self): - return self._pc - - class TestUnwinder(Unwinder): AMD64_RBP = 6 AMD64_RSP = 7