From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-io1-xd35.google.com (mail-io1-xd35.google.com [IPv6:2607:f8b0:4864:20::d35]) by sourceware.org (Postfix) with ESMTPS id 637993858C66 for ; Thu, 22 Jun 2023 16:19:32 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 637993858C66 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com Received: by mail-io1-xd35.google.com with SMTP id ca18e2360f4ac-780d6e6b037so3398139f.3 for ; Thu, 22 Jun 2023 09:19:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1687450771; x=1690042771; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=csWcGd/ANJoSkF7/XsT6obMScOXFYEkhUE2UHFmYc6g=; b=f4zhVAC6EAcEZFcwBH43nWz4xwqdk+CYBU8J4RbwHXyVIvoh+Ctd/KxjvqfxLjUz01 W5Rf0QSvrTvEk12AiHmThQGEjVpf5l3+5ax4wTvn2/6JlXH5WmtSXnLjk530CDT1C5Oa WGYOwwFW8zdwCzJ/04ys73ox8uWZnajuEnz/MmmTGgXayUTnONeYc/7laax5539/nP8C sy910++j5nnTSpJhPhaEnj3BfrvwGMxE98KHjX/VR8idR04qruhyl53aq4kWXQMMTB6F z0f1700v6ZqqOf9wVxy0cN/vfUV0vTV0uhfCbwqBy/G30TyuZVhmgcLuYVXCC+aBMS0q jNcw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687450771; x=1690042771; h=to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=csWcGd/ANJoSkF7/XsT6obMScOXFYEkhUE2UHFmYc6g=; b=BB8fTUdC0HThtw1RUufw1PTz81TGDcoMd5gGpmN1RP9LLRG7S0gdsuCHxGicOl1Giw d9npMXkrKiZzcgCrkj2ZEd6mZZGBCImEysWuawVOaeTHBOB9Ig5e7uC5WEvVHnE11plA pWqbh3ytsRkrioH2jhpjAs6qRHowT604BpD0DxDKevDseNnGTwWSwqupSacsgVwuSh6S pTPweh2rujrDjpYribmNhw5KenSVw3xzHqttB4trtHIP+UIe0MbTfZPsrZ1fC5/2N0iL lBMmyC1J2xwWo0BjW5V9x9gxdMiQB99Cwn/FuHxtAFBh2H1xMrfUTrHgPJ6o07GVykhP yISw== X-Gm-Message-State: AC+VfDzkSk4sE6JHUnfxSPAcpJjLU/aNp7hACbkaSfHjiOyu/thS8ZqV odD43Ab0JWjqC2WynrUHfXfpHzLK9BZStYHU5otQmg== X-Google-Smtp-Source: ACHHUZ5XojHTquUMRWIFjl8GoL7NChewMlbuyCWSf/sTXaHzZJD5qdsAQe19sNrWmvfnhuCjdG1QTg== X-Received: by 2002:a5d:8455:0:b0:780:c221:89e9 with SMTP id w21-20020a5d8455000000b00780c22189e9mr4200380ior.5.1687450771360; Thu, 22 Jun 2023 09:19:31 -0700 (PDT) Received: from localhost.localdomain (75-166-136-83.hlrn.qwest.net. [75.166.136.83]) by smtp.gmail.com with ESMTPSA id y8-20020a6bd808000000b0077ac2261248sm2201987iob.5.2023.06.22.09.19.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 22 Jun 2023 09:19:31 -0700 (PDT) From: Tom Tromey Date: Thu, 22 Jun 2023 10:19:31 -0600 Subject: [PATCH v2 5/7] Reimplement DAP stack traces using frame filters MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20230614-dap-frame-decor-v2-5-10628dfa6b60@adacore.com> References: <20230614-dap-frame-decor-v2-0-10628dfa6b60@adacore.com> In-Reply-To: <20230614-dap-frame-decor-v2-0-10628dfa6b60@adacore.com> To: gdb-patches@sourceware.org X-Mailer: b4 0.12.2 X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: This reimplements DAP stack traces using frame filters. This slightly simplifies the code, because frame filters and DAP were already doing some similar work. This also renames RegisterReference and ScopeReference to make it clear that these are private (and so changes don't have to worry about other files). Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30468 --- gdb/python/lib/gdb/dap/bt.py | 81 ++++++++++++++++--------------------- gdb/python/lib/gdb/dap/evaluate.py | 11 ++--- gdb/python/lib/gdb/dap/frames.py | 7 ++++ gdb/python/lib/gdb/dap/scopes.py | 83 +++++++++++++++----------------------- 4 files changed, 79 insertions(+), 103 deletions(-) diff --git a/gdb/python/lib/gdb/dap/bt.py b/gdb/python/lib/gdb/dap/bt.py index 4439b428926..0350a3bb6d5 100644 --- a/gdb/python/lib/gdb/dap/bt.py +++ b/gdb/python/lib/gdb/dap/bt.py @@ -15,67 +15,56 @@ import gdb import os +import itertools +from gdb.frames import frame_iterator +from gdb.FrameIterator import FrameIterator +from gdb.FrameDecorator import FrameDecorator from .frames import frame_id from .server import request, capability from .startup import send_gdb_with_response, in_gdb_thread from .state import set_thread -# Helper function to safely get the name of a frame as a string. -@in_gdb_thread -def _frame_name(frame): - name = frame.name() - if name is None: - name = "???" - return name - - -# Helper function to get a frame's SAL without an error. -@in_gdb_thread -def _safe_sal(frame): - try: - return frame.find_sal() - except gdb.error: - return None - - # Helper function to compute a stack trace. @in_gdb_thread def _backtrace(thread_id, levels, startFrame): set_thread(thread_id) frames = [] - current_number = 0 + if levels == 0: + # Zero means all remaining frames. + high = -1 + else: + # frame_iterator uses an inclusive range, so subtract one. + high = startFrame + levels - 1 try: - current_frame = gdb.newest_frame() + frame_iter = frame_iterator(gdb.newest_frame(), startFrame, high) except gdb.error: - current_frame = None - while current_frame is not None and (levels == 0 or len(frames) < levels): - # This condition handles the startFrame==0 case as well. - if current_number >= startFrame: - newframe = { - "id": frame_id(current_frame), - "name": _frame_name(current_frame), - # This must always be supplied, but we will set it - # correctly later if that is possible. - "line": 0, - # GDB doesn't support columns. - "column": 0, - "instructionPointerReference": hex(current_frame.pc()), + frame_iter = () + for current_frame in frame_iter: + newframe = { + "id": frame_id(current_frame), + "name": current_frame.function(), + # This must always be supplied, but we will set it + # correctly later if that is possible. + "line": 0, + # GDB doesn't support columns. + "column": 0, + "instructionPointerReference": hex(current_frame.address()), + } + line = current_frame.line() + if line is not None: + newframe["line"] = line + filename = current_frame.filename() + if filename is not None: + newframe["source"] = { + "name": os.path.basename(filename), + "path": filename, + # We probably don't need this but it doesn't hurt + # to be explicit. + "sourceReference": 0, } - sal = _safe_sal(current_frame) - if sal is not None and sal.symtab is not None: - newframe["source"] = { - "name": os.path.basename(sal.symtab.filename), - "path": sal.symtab.filename, - # We probably don't need this but it doesn't hurt - # to be explicit. - "sourceReference": 0, - } - newframe["line"] = sal.line - frames.append(newframe) - current_number = current_number + 1 - current_frame = current_frame.older() + frames.append(newframe) # Note that we do not calculate totalFrames here. Its absence # tells the client that it may simply ask for frames until a # response yields fewer frames than requested. diff --git a/gdb/python/lib/gdb/dap/evaluate.py b/gdb/python/lib/gdb/dap/evaluate.py index 651a4046a34..456b03e39cc 100644 --- a/gdb/python/lib/gdb/dap/evaluate.py +++ b/gdb/python/lib/gdb/dap/evaluate.py @@ -19,7 +19,7 @@ import gdb.printing # This is deprecated in 3.9, but required in older versions. from typing import Optional -from .frames import frame_for_id +from .frames import select_frame from .server import capability, request, client_bool_capability from .startup import send_gdb_with_response, in_gdb_thread from .varref import find_variable, VariableReference @@ -35,8 +35,7 @@ class EvaluateResult(VariableReference): def _evaluate(expr, frame_id): global_context = True if frame_id is not None: - frame = frame_for_id(frame_id) - frame.select() + select_frame(frame_id) global_context = False val = gdb.parse_and_eval(expr, global_context=global_context) ref = EvaluateResult(val) @@ -58,8 +57,7 @@ def _eval_for_hover(expr, frame_id): def _set_expression(expression, value, frame_id): global_context = True if frame_id is not None: - frame = frame_for_id(frame_id) - frame.select() + select_frame(frame_id) global_context = False lhs = gdb.parse_and_eval(expression, global_context=global_context) rhs = gdb.parse_and_eval(value, global_context=global_context) @@ -71,8 +69,7 @@ def _set_expression(expression, value, frame_id): @in_gdb_thread def _repl(command, frame_id): if frame_id is not None: - frame = frame_for_id(frame_id) - frame.select() + select_frame(frame_id) val = gdb.execute(command, from_tty=True, to_string=True) return { "result": val, diff --git a/gdb/python/lib/gdb/dap/frames.py b/gdb/python/lib/gdb/dap/frames.py index 08209d0b361..1d2d1371354 100644 --- a/gdb/python/lib/gdb/dap/frames.py +++ b/gdb/python/lib/gdb/dap/frames.py @@ -52,3 +52,10 @@ def frame_for_id(id): """Given a frame identifier ID, return the corresponding frame.""" global _all_frames return _all_frames[id] + + +@in_gdb_thread +def select_frame(id): + """Given a frame identifier ID, select the corresponding frame.""" + frame = frame_for_id(id) + frame.inferior_frame().select() diff --git a/gdb/python/lib/gdb/dap/scopes.py b/gdb/python/lib/gdb/dap/scopes.py index 9b80dd9ce80..1687094c4ce 100644 --- a/gdb/python/lib/gdb/dap/scopes.py +++ b/gdb/python/lib/gdb/dap/scopes.py @@ -21,41 +21,17 @@ from .server import request from .varref import BaseReference -# Helper function to return a frame's block without error. -@in_gdb_thread -def _safe_block(frame): - try: - return frame.block() - except gdb.error: - return None - - -# Helper function to return two lists of variables of block, up to the -# enclosing function. The result is of the form (ARGS, LOCALS), where -# each element is itself a list. -@in_gdb_thread -def _block_vars(block): - args = [] - locs = [] - while True: - for var in block: - if var.is_argument: - args.append(var) - elif var.is_variable or var.is_constant: - locs.append(var) - if block.function is not None: - break - block = block.superblock - return (args, locs) - - -class ScopeReference(BaseReference): +class _ScopeReference(BaseReference): def __init__(self, name, hint, frame, var_list): super().__init__(name) self.hint = hint self.frame = frame + self.inf_frame = frame.inferior_frame() self.func = frame.function() - self.var_list = var_list + self.line = frame.line() + # VAR_LIST might be any kind of iterator, but it's convenient + # here if it is just a collection. + self.var_list = tuple(var_list) def to_object(self): result = super().to_object() @@ -63,8 +39,8 @@ class ScopeReference(BaseReference): # How would we know? result["expensive"] = False result["namedVariables"] = len(self.var_list) - if self.func is not None: - result["line"] = self.func.line + if self.line is not None: + result["line"] = self.line # FIXME construct a Source object return result @@ -73,38 +49,45 @@ class ScopeReference(BaseReference): @in_gdb_thread def fetch_one_child(self, idx): + # Here SYM will conform to the SymValueWrapper interface. sym = self.var_list[idx] - if sym.needs_frame: - val = sym.value(self.frame) - else: - val = sym.value() - return (sym.print_name, val) - - -class RegisterReference(ScopeReference): + name = str(sym.symbol()) + val = sym.value() + if val is None: + # No synthetic value, so must read the symbol value + # ourselves. + val = sym.symbol().value(self.inf_frame) + elif not isinstance(val, gdb.Value): + val = gdb.Value(val) + return (name, val) + + +class _RegisterReference(_ScopeReference): def __init__(self, name, frame): super().__init__( - name, "registers", frame, list(frame.architecture().registers()) + name, "registers", frame, frame.inferior_frame().architecture().registers() ) @in_gdb_thread def fetch_one_child(self, idx): - return (self.var_list[idx].name, self.frame.read_register(self.var_list[idx])) + return ( + self.var_list[idx].name, + self.inf_frame.read_register(self.var_list[idx]), + ) # Helper function to create a DAP scopes for a given frame ID. @in_gdb_thread def _get_scope(id): frame = frame_for_id(id) - block = _safe_block(frame) scopes = [] - if block is not None: - (args, locs) = _block_vars(block) - if args: - scopes.append(ScopeReference("Arguments", "arguments", frame, args)) - if locs: - scopes.append(ScopeReference("Locals", "locals", frame, locs)) - scopes.append(RegisterReference("Registers", frame)) + args = frame.frame_args() + if args: + scopes.append(_ScopeReference("Arguments", "arguments", frame, args)) + locs = frame.frame_locals() + if locs: + scopes.append(_ScopeReference("Locals", "locals", frame, locs)) + scopes.append(_RegisterReference("Registers", frame)) return [x.to_object() for x in scopes] -- 2.40.1