From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com [IPv6:2a00:1450:4864:20::429]) by sourceware.org (Postfix) with ESMTPS id C94523858429 for ; Sat, 11 Sep 2021 16:03:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C94523858429 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-wr1-x429.google.com with SMTP id x6so7329556wrv.13 for ; Sat, 11 Sep 2021 09:03:31 -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=ZG6/Y1WgPsXIjJZZu5QdDOVapSd9jisbne/WnWuuQHU=; b=GOV9onuBQXFARADNjD2Zt4d5cyHgpB7zEL2Mi71feMoOiCl6EokAi/JdKVSzeZh862 SK30bYSmC/gBkFVl4Vb8CJvNqlbqYuWOu/LWK9BlLM29qm+y4yABVOPBkMnKvTq3zyHn slD4fbe/hhPESNQDoqvD0+1MVdq/KQYbneFBWqDF2lXavJaWWD6MkSOpVE/Jr7mi2my4 wrqCIQynAFFbe0UhSUUU4R4wy2AZFbM5ofs4auh/Bu7JOkm08DX+bRyW6i9EnTCd6nAz WKvdyoBOTO59mI9Alaaq/KvciB6Ht01BNGCH1/stVPjbrdsB9K9rKqu25DC8JDo2HWH1 cpEQ== 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=ZG6/Y1WgPsXIjJZZu5QdDOVapSd9jisbne/WnWuuQHU=; b=4XXy4IRwWJeEIngbORnBNch+VIJgtpS+qzDlik/bjxZJhh8kIytYNKm/7S41P9scui bftPzGPNzto2oF/UlbIJ1Dp0cFIw+shinh24s+6zs8ojDuQOXvSKu/UkS9EgRtlKEeqb d9nYMd04I+l9CTts9P0wMXKprzZRfh76ZcG6Fn3oFpUCpdRGsrnEiTYzliINHAxo7g8F LKvaVlhdloWjDeo5lQX+Ze/tCp0Xxx/dTUwZgwwev2cDIUygZsZlfKZJzanzLuiOPLoy LR4u1Tnv+0kcitzE3ne268joHXWwAkwXwv3Ce8QLY+DMrDrtMQy9G+Jb7BtVHrJSwxbt TeXw== X-Gm-Message-State: AOAM533aJp1zHCa1LZ40tTcTnYMsf1CIeT0ek+dhqk8qI8PdJrwG12yy KLo17MkI+yRcNXx1uoyjOB2oaDpxxmXFWg== X-Google-Smtp-Source: ABdhPJz9YzKCTRVO8Z3eoL50AhuCvLM6wJ887Yd2B5ID7vkRPuuN2Sw0I2kSc8hhdASfWCh2jqkuew== X-Received: by 2002:a05:6000:128d:: with SMTP id f13mr3602750wrx.244.1631376210465; Sat, 11 Sep 2021 09:03:30 -0700 (PDT) Received: from localhost (host86-153-58-62.range86-153.btcentralplus.com. [86.153.58.62]) by smtp.gmail.com with ESMTPSA id n10sm2063673wrt.78.2021.09.11.09.03.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 11 Sep 2021 09:03:30 -0700 (PDT) From: Andrew Burgess To: gdb-patches@sourceware.org Subject: [PATCH 3/3] gdb/python: add TargetConnection.send_remote_packet method Date: Sat, 11 Sep 2021 17:03:25 +0100 Message-Id: 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=-10.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_BARRACUDACENTRAL, 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: Sat, 11 Sep 2021 16:03:34 -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 | 23 +++++- 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, 278 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 6d8739573b3..86adbb9ae96 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -33,6 +33,12 @@ maint show backtrace-on-fatal-signal ** 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 58479ef3ed6..2bfcefd028c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -39231,6 +39231,7 @@ disabled. @end table +@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 9f92aa791c4..6ecd82edb94 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -5976,7 +5976,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, @@ -5988,6 +5988,27 @@ an exception if the connection is invalid. @end defun +@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 string, to the remote target and returns the response +packet as a string. + +This is equivalent to the @code{maintenance packet} command +(@pxref{maint packet,,maintenance packet command}). + +If the connection is of any other type then an exception of type +@code{gdb.error} is thrown. 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. +@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..ddc4e05d63f 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 (), NULL)); + } + + /* 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 NULL; + } +} + /* 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