From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id +LAZBrqqsmCyBAAAWB0awg (envelope-from ) for ; Sat, 29 May 2021 16:57:30 -0400 Received: by simark.ca (Postfix, from userid 112) id 1372A1F13D; Sat, 29 May 2021 16:57:30 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-0.5 required=5.0 tests=DKIM_SIGNED, MAILING_LIST_MULTI,RDNS_DYNAMIC,T_DKIM_INVALID,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id A38BE1EE1B for ; Sat, 29 May 2021 16:57:25 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1B07E388A40D; Sat, 29 May 2021 20:57:25 +0000 (GMT) Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by sourceware.org (Postfix) with ESMTPS id E39C43886C6B for ; Sat, 29 May 2021 20:57:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org E39C43886C6B Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=andrew.burgess@embecosm.com Received: by mail-wm1-x32b.google.com with SMTP id f17so2544566wmf.2 for ; Sat, 29 May 2021 13:57:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=embecosm.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pJa25sS6+fQgH7VjyRnhFIuyYJ1wPrNhO6TmgXN+Qrw=; b=QNgSUuffhlYIB9PKDuO3fOK8AdqBZOIfnxO7nheRnd5eSNFMApeoPPvNJMR9+wr3Q6 l1sU8/Kebd+njQ0gF/3oGAZx8EcFYL/kN+gRsz+sEnFS+nfPbQ7GUq4HoQKvyATmfTPx S3Fs2ky2fJqiWmARyNAfdRoMDrqTwDk8idJVRAQr1vJZ4epQ/QbgIvW80r/l4HxGJ4st OLD3JgaQikwKJMyNChawUzyGit2xfuMRDN1nvBrefxQAMFijecJdsBW1JbJlfTxr3iDu ey1Q8YtgEFeW+DiyROgxvGIjkG66PwVBSTTZd17e6BSx0sa4v9ZTnec9yPtEP8ESd9p9 XwdA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pJa25sS6+fQgH7VjyRnhFIuyYJ1wPrNhO6TmgXN+Qrw=; b=b9PGHpMUEVQvb/pTUX0Fiw2kqPattqyVDevbPVvoUK/bFV4oovqQmDl2vbbB4Dy70I jX/g6XEPOnutQb/QeAaLEFJFvjO3VJXKwaMVt0wwf2wtR2mAHZUQvNbhfXkqb7NkzI3H RP7p31Id+gb9MnA67cFX9G2SPN/ijwrldV0Ul0fI1selkOewLvTWCKaSJ07A9FYUuxxi E5C7MU1Karf1l//kch0V3L7KeVzDRAxi3xtrKPCxoci3ciTCWATLG/dnWQ9LtY11tj15 T5oGDJmaW4p9hA2Grr7bwvAnr+yn+xWt9z0koup2zzT41Sw2/o8vmazABwOEzwmkpwlI nXeA== X-Gm-Message-State: AOAM5322UUEVY8ssIpVPR4N2x4dgzKG0jg6Qo1qsekQfFg1Rs+YZfxSD QR47+QMCIZtBLAj56Fal9ap7hXnFibcw2g== X-Google-Smtp-Source: ABdhPJzDOp0DDJS+CuH+gIgTGsm2PpzHxcdEhvWE71t61UyzOpiTrCOLekBrU7YVVilJbtu8lNUU4A== X-Received: by 2002:a1c:98d0:: with SMTP id a199mr5603684wme.22.1622321840727; Sat, 29 May 2021 13:57:20 -0700 (PDT) Received: from localhost (host109-151-46-70.range109-151.btcentralplus.com. [109.151.46.70]) by smtp.gmail.com with ESMTPSA id 11sm10676175wmo.24.2021.05.29.13.57.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 May 2021 13:57:20 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Subject: [PATCH 3/5] gdb/python: add PendingFrame.level and Frame.level methods Date: Sat, 29 May 2021 21:57:12 +0100 Message-Id: <5d6ac8c94e5c473d533d945ba9759af2d8ae57a6.1622321523.git.andrew.burgess@embecosm.com> X-Mailer: git-send-email 2.25.4 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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: , Errors-To: gdb-patches-bounces@sourceware.org Sender: "Gdb-patches" Add new methods to the PendingFrame and Frame classes to obtain the stack frame level for each object. The use of 'level' as the method name is consistent with the existing attribute RecordFunctionSegment.level (though this is an attribute rather than a method). For Frame/PendingFrame I went with methods as these classes currently only use methods, including for simple data like architecture, so I want to be consistent with this interface. gdb/ChangeLog: * python/py-frame.c (frapy_level): New function. (frame_object_methods): Register 'level' method. * python/py-unwind.c (pending_framepy_level): New function. (pending_frame_object_methods): Register 'level' method. gdb/doc/ChangeLog: * python.texi (Unwinding Frames in Python): Mention PendingFrame.level. (Frames In Python): Mention Frame.level. gdb/testsuite/ChangeLog: * gdb.python/py-frame.exp: Add Frame.level tests. * gdb.python/py-pending-frame-level.c: New file. * gdb.python/py-pending-frame-level.exp: New file. * gdb.python/py-pending-frame-level.py: New file. --- gdb/ChangeLog | 7 ++ gdb/doc/ChangeLog | 6 ++ gdb/doc/python.texi | 9 +++ gdb/python/py-frame.c | 23 +++++++ gdb/python/py-unwind.c | 19 ++++++ gdb/testsuite/ChangeLog | 7 ++ gdb/testsuite/gdb.python/py-frame.exp | 11 ++++ .../gdb.python/py-pending-frame-level.c | 49 ++++++++++++++ .../gdb.python/py-pending-frame-level.exp | 65 +++++++++++++++++++ .../gdb.python/py-pending-frame-level.py | 55 ++++++++++++++++ 10 files changed, 251 insertions(+) create mode 100644 gdb/testsuite/gdb.python/py-pending-frame-level.c create mode 100644 gdb/testsuite/gdb.python/py-pending-frame-level.exp create mode 100644 gdb/testsuite/gdb.python/py-pending-frame-level.py diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 23e6ac666ff..b7e16351a5d 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -2605,6 +2605,11 @@ the particular frame being unwound. @end defun +@defun PendingFrame.level () +Return an integer, the stack frame level for this frame. +@xref{Frames, ,Stack Frames}. +@end defun + @subheading Unwinder Output: UnwindInfo Use @code{PendingFrame.create_unwind_info} method described above to @@ -4813,6 +4818,10 @@ Stack}. @end defun +@defun Frame.level () +Return an integer, the stack frame level for this frame. @xref{Frames, ,Stack Frames}. +@end defun + @node Blocks In Python @subsubsection Accessing blocks from Python diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c index c8eab5291ea..4f218c40367 100644 --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -577,6 +577,27 @@ frapy_select (PyObject *self, PyObject *args) Py_RETURN_NONE; } +/* The stack frame level for this frame. */ + +static PyObject * +frapy_level (PyObject *self, PyObject *args) +{ + struct frame_info *fi; + + try + { + FRAPY_REQUIRE_VALID (self, fi); + + return gdb_py_object_from_int (frame_relative_level (fi)).release (); + } + catch (const gdb_exception &except) + { + GDB_PY_HANDLE_EXCEPTION (except); + } + + Py_RETURN_NONE; +} + /* Implementation of gdb.newest_frame () -> gdb.Frame. Returns the newest frame object. */ @@ -748,6 +769,8 @@ Return the frame's symtab and line." }, Return the value of the variable in this frame." }, { "select", frapy_select, METH_NOARGS, "Select this frame as the user's current frame." }, + { "level", frapy_level, METH_NOARGS, + "The stack level of this frame." }, {NULL} /* Sentinel */ }; diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index d6e2f85dbc1..ff1a7e922a7 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -463,6 +463,23 @@ pending_framepy_architecture (PyObject *self, PyObject *args) return gdbarch_to_arch_object (pending_frame->gdbarch); } +/* Implementation of PendingFrame.level (self) -> Integer. */ + +static PyObject * +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; + } + int level = frame_relative_level (pending_frame->frame_info); + return gdb_py_object_from_int (level).release (); +} + /* frame_unwind.this_id method. */ static void @@ -704,6 +721,8 @@ static PyMethodDef pending_frame_object_methods[] = pending_framepy_architecture, METH_NOARGS, "architecture () -> gdb.Architecture\n" "The architecture for this PendingFrame." }, + { "level", pending_framepy_level, METH_NOARGS, + "The stack level of this frame." }, {NULL} /* Sentinel */ }; diff --git a/gdb/testsuite/gdb.python/py-frame.exp b/gdb/testsuite/gdb.python/py-frame.exp index a6a5c0de726..05c7fb00dfd 100644 --- a/gdb/testsuite/gdb.python/py-frame.exp +++ b/gdb/testsuite/gdb.python/py-frame.exp @@ -70,6 +70,17 @@ gdb_test "up" ".*" "" gdb_py_test_silent_cmd "python f1 = gdb.selected_frame ()" "get second frame" 0 gdb_py_test_silent_cmd "python f0 = f1.newer ()" "get first frame" 0 +gdb_py_test_silent_cmd "python f2 = f1.older ()" "get last frame" 0 + +# Check the Frame.level method. +gdb_test "python print ('bframe.level = %d' % bframe.level ())" \ + "bframe\\.level = 0" +gdb_test "python print ('f0.level = %d' % f0.level ())" \ + "f0\\.level = 0" +gdb_test "python print ('f1.level = %d' % f1.level ())" \ + "f1\\.level = 1" +gdb_test "python print ('f2.level = %d' % f2.level ())" \ + "f2\\.level = 2" gdb_test "python print (f1 == gdb.newest_frame())" False \ "selected frame -vs- newest frame" diff --git a/gdb/testsuite/gdb.python/py-pending-frame-level.c b/gdb/testsuite/gdb.python/py-pending-frame-level.c new file mode 100644 index 00000000000..5e5495c1d71 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-pending-frame-level.c @@ -0,0 +1,49 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2021 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +volatile int global_var; + +void __attribute__ ((noinline)) +f0 (void) +{ + ++global_var; /* Break here. */ +} + +void __attribute__ ((noinline)) +f1 (void) +{ + f0 (); +} + +void __attribute__ ((noinline)) +f2 (void) +{ + f1 (); +} + +void __attribute__ ((noinline)) +f3 (void) +{ + f2 (); +} + +int +main (void) +{ + f3 (); + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-pending-frame-level.exp b/gdb/testsuite/gdb.python/py-pending-frame-level.exp new file mode 100644 index 00000000000..1aadcaeacae --- /dev/null +++ b/gdb/testsuite/gdb.python/py-pending-frame-level.exp @@ -0,0 +1,65 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test gdb.PendingFrame.level method. + +load_lib gdb-python.exp + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if ![runto_main] then { + fail "can't run to main" + return 0 +} + +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +gdb_breakpoint [gdb_get_line_number "Break here"] +gdb_continue_to_breakpoint "stop at test breakpoint" + +# An initial look at the stack to ensure it is correct. +gdb_test_sequence "bt" "Initial backtrace" { + "\\r\\n#0 \[^\r\n\]* f0 \\(\\) at " + "\\r\\n#1 \[^\r\n\]* f1 \\(\\) at " + "\\r\\n#2 \[^\r\n\]* f2 \\(\\) at " + "\\r\\n#3 \[^\r\n\]* f3 \\(\\) at " + "\\r\\n#4 \[^\r\n\]* main \\(\\) at " +} + +# Load the script containing the unwinder. +gdb_test_no_output "source ${pyfile}"\ + "import python scripts" + +# Now look at the stack again, we should see output from the Python +# unwinder mixed in. +gdb_test_sequence "bt" "Backtrace with extra Python output" { + "Func f0, Level 0" + "Func f1, Level 1" + "\\r\\n#0 \[^\r\n\]* f0 \\(\\) at " + "\\r\\n#1 \[^\r\n\]* f1 \\(\\) at " + "Func f2, Level 2" + "\\r\\n#2 \[^\r\n\]* f2 \\(\\) at " + "Func f3, Level 3" + "\\r\\n#3 \[^\r\n\]* f3 \\(\\) at " + "Func main, Level 4" + "\\r\\n#4 \[^\r\n\]* main \\(\\) at " +} diff --git a/gdb/testsuite/gdb.python/py-pending-frame-level.py b/gdb/testsuite/gdb.python/py-pending-frame-level.py new file mode 100644 index 00000000000..182edcdc0df --- /dev/null +++ b/gdb/testsuite/gdb.python/py-pending-frame-level.py @@ -0,0 +1,55 @@ +# Copyright (C) 2021 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import gdb +from gdb.unwinder import Unwinder + + +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): + def __init__(self): + Unwinder.__init__(self, "show level") + + def __call__(self, pending_frame): + pc_desc = pending_frame.architecture().registers().find("pc") + pc = pending_frame.read_register(pc_desc) + + block = gdb.block_for_pc(int(pc)) + if block == None: + return None + func = block.function + if func == None: + return None + + print("Func %s, Level %d" % (str(func), pending_frame.level())) + + # This unwinder never claims any frames. + return None + + +gdb.unwinder.register_unwinder(None, TestUnwinder(), True) -- 2.25.4