From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x335.google.com (mail-wm1-x335.google.com [IPv6:2a00:1450:4864:20::335]) by sourceware.org (Postfix) with ESMTPS id 582143858426 for ; Mon, 18 Oct 2021 09:46:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 582143858426 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=embecosm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=embecosm.com Received: by mail-wm1-x335.google.com with SMTP id s198-20020a1ca9cf000000b0030d6986ea9fso7936842wme.1 for ; Mon, 18 Oct 2021 02:46:08 -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=qz4OlOoLleBnYkY9PavBPtmkX3mIDOXfP0DfZxClxzI=; b=cHhfxZQ1C5+dPa5lxzbt0cibMfWegtBv2piUwQknDA0iGoEyHINkqOEYsOWGVP3txe GTco6MFsELTKWoQXQ/qKVLto1C7yb3Tab1lFsC589LWaaWwth/fdvi7JFEvqj2aiwI/S jOmejdzzaXA7RNHrcp/DO0/kBp+hypljRGzTyTWetTlGf17RXzCfyyhF648K+OjcdTsE CCtwM8JdIuFfsKooye13F327IBQ2uHSlsMXsr0M67oBtdVZQKOxnLXR1ZA1vb5Mt6McQ CQd4W8DFE0vLLgSJDeiTNfrkyPwTLL7AuMojEulJBQAW6xLqqXY74k5Ppbml43UmyJQu mtaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qz4OlOoLleBnYkY9PavBPtmkX3mIDOXfP0DfZxClxzI=; b=aSqYhs06bTHdNfukCv60Pr9JSoNj8/DwjjJV610OOdPmuM0073haMTLDLNIsKL4ESF DmBiicXsSn1l8G8yDpfKvJZu24tswri5cAduGSdk308uaPyKg/7itHS/KRDLpMrTMaDf aV9YJZCoU7HugnwaXtF8E+QiLUZfl7Y5ITQ5V6WIMi/atiOGNZeysSgjZAE2HR7WNIPb Yd89FXFEK+PdSvWI8jsOBGyqpNXGbWcvGuBTYCrkmLLAqWSlny531othbilgtCdHiN6A xbXjGVoLnJB2y9DKnBKt/50ma91utaeBJcz9oWpuaVHeBHP6laSuhW7xIYBt1xlsP9xc 2+ng== X-Gm-Message-State: AOAM533/UUY4JYalUGjn5C7UUn9buFzUF3pWiu8STevlzyaXnqtvtaj+ ArEVQspGBZSBL1uvlRIKpz7Nv/UwPiW6LQ== X-Google-Smtp-Source: ABdhPJw2JpmWUWjNJkOHxaAXa8FsWoqmIcGMcS2C8jpWnpz+SnH808tbXdOjiSBI0e6KnBryUyJAGw== X-Received: by 2002:a1c:9892:: with SMTP id a140mr29195538wme.187.1634550366921; Mon, 18 Oct 2021 02:46:06 -0700 (PDT) Received: from localhost (host212-140-123-151.range212-140.btcentralplus.com. [212.140.123.151]) by smtp.gmail.com with ESMTPSA id o6sm15198588wri.49.2021.10.18.02.46.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 18 Oct 2021 02:46:06 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Subject: [PATCHv2 3/3] gdb/python: add TargetConnection.send_remote_packet method Date: Mon, 18 Oct 2021 10:45:54 +0100 Message-Id: <4cb41fa4660ae5c39493c1d8985017938eaceb8d.1634550226.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-Spam-Status: No, score=-11.9 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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-List-Received-Date: Mon, 18 Oct 2021 09:46:11 -0000 This commit adds a new method to the gdb.TargetConnection object type: 'send_remote_packet'. This new method is equivalent to the 'maint packet' CLI command, it allows a custom packet to be sent to a remote target. Not all gdb.TargetConnection instances will support send_remote_packet, e.g. the 'native' target doesn't. If the send_remote_packet method is invoked on a TargetConnection that is not 'remote' or 'extended-remote' then an exception is thrown. The result of calling TargetConnection.send_remote_packet is a string containing the reply that came from the remote. --- gdb/NEWS | 6 ++ gdb/doc/gdb.texinfo | 1 + gdb/doc/python.texi | 22 ++++- gdb/python/py-connection.c | 90 +++++++++++++++++++++ gdb/testsuite/gdb.python/py-send-packet.c | 22 +++++ gdb/testsuite/gdb.python/py-send-packet.exp | 55 +++++++++++++ gdb/testsuite/gdb.python/py-send-packet.py | 82 +++++++++++++++++++ 7 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.python/py-send-packet.c create mode 100644 gdb/testsuite/gdb.python/py-send-packet.exp create mode 100644 gdb/testsuite/gdb.python/py-send-packet.py diff --git a/gdb/NEWS b/gdb/NEWS index 43b81df526d..a567098d13a 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -61,6 +61,12 @@ maint show internal-warning backtrace ** New gdb.connections() function that returns a list of all currently active connections. + ** New gdb.TargetConnection.send_remote_packet(STRING) method. This + is equivalent to the existing 'maint packet' CLI command; it + allows a user specified packet to be sent to the remote target. + If this method is called on a TargetConnection that is not of + type 'remote' or 'extended-remote' then an exception is thrown. + *** Changes in GDB 11 * The 'set disassembler-options' command now supports specifying options diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 631a7c03b31..89cf86210ab 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -39269,6 +39269,7 @@ error stream. This is @samp{on} by default for @code{internal-error} and @samp{off} by default for @code{internal-warning}. +@anchor{maint packet} @kindex maint packet @item maint packet @var{text} If @value{GDBN} is talking to an inferior via the serial protocol, diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index f81707eb002..6e8ab3a5a5c 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -5989,7 +5989,7 @@ @code{gdb.Inferior.connection} attribute (@pxref{gdbpy_inferior_connection,,gdb.Inferior.connection}). -A @code{gdb.TargetConnection} has the following method: +A @code{gdb.TargetConnection} has the following methods: @defun TargetConnection.is_valid () Return @code{True} if the @code{gdb.TargetConnection} object is valid, @@ -6001,6 +6001,26 @@ an exception if the connection is invalid. @end defun +@kindex maint packet +@defun TargetConnection.send_remote_packet (@var{packet}) +When the connection object is using @value{GDBN}'s remote serial +protocol (@pxref{Remote Protocol}), i.e.@: the +@code{TargetConnection.type} is @samp{remote} or +@samp{extended-remote}, then this method sends @var{packet}, which +should be a non-empty string, to the remote target and returns the +response packet as a string. For any other connection type, calling +this method will throw a @code{gdb.error} exception. If @var{packet} +is not a string, or is the empty string, then an exception of type +@code{gdb.error} is thrown. + +The prefix, suffix, and checksum (as required by the remote serial +protocol) are automatically added to the outgoing packet, and removed +from the incoming packet before the reply is returned. + +This is equivalent to the @code{maintenance packet} command +(@pxref{maint packet}). +@end defun + A @code{gdb.TargetConnection} has the following read-only properties: @defvar TargetConnection.num diff --git a/gdb/python/py-connection.c b/gdb/python/py-connection.c index f75a8735148..43797d48a94 100644 --- a/gdb/python/py-connection.c +++ b/gdb/python/py-connection.c @@ -26,6 +26,8 @@ #include "py-events.h" #include "py-event.h" #include "arch-utils.h" +#include "remote.h" +#include "charset.h" #include @@ -286,6 +288,90 @@ gdbpy_initialize_connection (void) return 0; } +/* Set of callbacks used to implement gdb.send_remote_packet. */ + +struct py_send_packet_callbacks : public send_remote_packet_callbacks +{ + /* Constructor, initialise the result to None. */ + + py_send_packet_callbacks () + : m_result (Py_None) + { /* Nothing. */ } + + /* There's nothing to do when the packet is sent. */ + + void sending (const char *args) override + { /* Nothing. */ } + + /* When the result is returned create a Python string and assign this + into the result member variable. */ + + void received (const gdb::char_vector &buf) override + { + /* Return None when we don't get back a useful result. */ + if (buf.data ()[0] != '\0') + m_result = gdbpy_ref<> (PyUnicode_Decode (buf.data (), + strlen (buf.data ()), + host_charset (), nullptr)); + } + + /* Get a reference to the result as a Python object. */ + + gdbpy_ref<> result () const + { + return m_result; + } + +private: + + /* A reference to a valid result value. This is initialized in the + constructor, and so will always point to a valid value, even if this + is just None. */ + + gdbpy_ref<> m_result; +}; + +/* Implement TargetConnection.send_remote_packet function. Send a packet + to the target identified by SELF. The connection must still be valid, + and must be a remote or extended-remote connection otherwise an + exception will be thrown. */ + +static PyObject * +connpy_send_remote_packet (PyObject *self, PyObject *args, PyObject *kw) +{ + connection_object *conn = (connection_object *) self; + + CONNPY_REQUIRE_VALID (conn); + + static const char *keywords[] = {"packet", nullptr}; + const char *packet_str = nullptr; + + if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s", keywords, + &packet_str)) + return nullptr; + + if (packet_str == nullptr || *packet_str == '\0') + { + PyErr_SetString (PyExc_ValueError, _("Invalid remote packet")); + return nullptr; + } + + try + { + scoped_restore_current_thread restore_thread; + switch_to_target_no_thread (conn->target); + + py_send_packet_callbacks callbacks; + send_remote_packet (packet_str, &callbacks); + return callbacks.result ().release (); + } + catch (const gdb_exception &except) + { + gdbpy_convert_exception (except); + return nullptr; + } +} + /* Global initialization for this file. */ void _initialize_py_connection (); @@ -303,6 +389,10 @@ static PyMethodDef connection_object_methods[] = { "is_valid", connpy_is_valid, METH_NOARGS, "is_valid () -> Boolean.\n\ Return true if this TargetConnection is valid, false if not." }, + { "send_remote_packet", (PyCFunction) connpy_send_remote_packet, + METH_VARARGS | METH_KEYWORDS, + "send_remote_packet (PACKET) -> String\n\ +Send PACKET to a remote target, return the reply as a string." }, { NULL } }; diff --git a/gdb/testsuite/gdb.python/py-send-packet.c b/gdb/testsuite/gdb.python/py-send-packet.c new file mode 100644 index 00000000000..9811b15f06d --- /dev/null +++ b/gdb/testsuite/gdb.python/py-send-packet.c @@ -0,0 +1,22 @@ +/* This testcase 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 . */ + +int +main () +{ + return 0; +} diff --git a/gdb/testsuite/gdb.python/py-send-packet.exp b/gdb/testsuite/gdb.python/py-send-packet.exp new file mode 100644 index 00000000000..81001343f0c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-send-packet.exp @@ -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 . + +# Test the gdb.TargetConnection.send_remote_packet API. This is done +# by connecting to a remote target and fetching the thread list in two +# ways, first, we manually send the packets required to read the +# thread list using gdb.TargetConnection.send_remote_packet, then we +# compare the results to the thread list using the standard API calls. + +load_lib gdb-python.exp +load_lib gdbserver-support.exp + +standard_testfile + +if {[skip_gdbserver_tests]} { + return 0 +} + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +gdb_exit +gdb_start + +if { [skip_python_tests] } { + return 0 +} + +# Make sure we're disconnected, in case we're testing with an +# extended-remote board, therefore already connected. +gdb_test "disconnect" ".*" + +gdbserver_run "" + +# Source the python script. +set remote_python_file [gdb_remote_download host \ + ${srcdir}/${subdir}/${testfile}.py] +gdb_test "source $remote_python_file" "Sourcing complete\\." \ + "source ${testfile}.py script" + +# The test is actually written in the Python script. Run it now. +gdb_test "python run_send_packet_test()" "Send packet test passed" diff --git a/gdb/testsuite/gdb.python/py-send-packet.py b/gdb/testsuite/gdb.python/py-send-packet.py new file mode 100644 index 00000000000..f4dc5734057 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-send-packet.py @@ -0,0 +1,82 @@ +# 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 xml.etree.ElementTree as ET +import gdb + +# Make use of gdb.TargetConnection.send_remote_packet to fetch the +# thread list from the remote target. +# +# Sending existing serial protocol packets like this is not a good +# idea, there should be better ways to get this information using an +# official API, this is just being used as a test case. +# +# Really, the send_remote_packet API would be used to send target +# specific packets to the target, but these are, by definition, target +# specific, so hard to test in a general testsuite. +def get_thread_list_str(): + start_pos = 0 + thread_desc = "" + try: + while True: + conn = gdb.selected_inferior().connection + str = conn.send_remote_packet("qXfer:threads:read::%d,200" % start_pos) + start_pos += 200 + c = str[0] + str = str[1:] + thread_desc += str + if c == "l": + break + return thread_desc + except: + return None + + +# Use gdb.TargetConnection.send_remote_packet to manually fetch the +# thread list, then extract the thread list using the gdb.Inferior and +# gdb.InferiorThread API. Compare the two results to ensure we +# managed to successfully read the thread list from the remote. +def run_send_packet_test(): + # Find the IDs of all current threads. + all_threads = {} + for inf in gdb.inferiors(): + for thr in inf.threads(): + id = "p%x.%x" % (thr.ptid[0], thr.ptid[1]) + all_threads[id] = False + + # Now fetch the thread list from the remote, and parse the XML. + str = get_thread_list_str() + threads_xml = ET.fromstring(str) + + # Look over all threads in the XML list and check we expected to + # find them, mark the ones we do find. + for thr in threads_xml: + id = thr.get("id") + if not id in all_threads: + raise "found unexpected thread in remote thread list" + else: + all_threads[id] = True + + # Check that all the threads were found in the XML list. + for id in all_threads: + if not all_threads[id]: + raise "thread missingt from remote thread list" + + # Test complete. + print("Send packet test passed") + + +# Just to indicate the file was sourced correctly. +print("Sourcing complete.") -- 2.25.4