f956c38cd7
* [BUGFIX] Respect infinite-timed session start timeouts. * When debugging, the intended behavior is to set the session start timeout to infinite to allow the user to configure the debugger. * At present, if a session start retry timeout is defined, the current logic will bail after the retry timeout expires. * This change makes the session start logic retry forever, once per retry timeout. * Document RPCEndpoint::Create. * Add stm32f746xx to tvm.target.micro() call; fix parameter name. * This API is expected to just be used with positional args, not kwargs, so this change isn't expected to cause any breakage. * model is more inline with the rest of the file, given TVM Target Specification RFC. * [BUGFIX] If session start fails, exit transport context manager. * If an error occurred during session setup, then complex transports e.g. DebugWrapperTransport would not de-initialize. * Align transport writes/reads in TransportLogger * fix syntax errors which were not exercised in previous PR * Remove microTVM logic from standard RPC server, add debug shell. * microTVM uses the host RPC server as a way to launch a debugger in a dedicated, separate terminal window. microTVM needs to be able to launch the debugger itself, because its model of the device flash/debug flow separates these two things into distinct operations implemented by shell commands (for maximum portability across frameworks). * microTVM can be configured to launch the debugger (e.g. GDB) in the same terminal as is used for flashing, but this is sub-optimal because then it hides any logs emitted by the device. * Using the standard RPC server was hard because GDB expects the user to issue SIGINT to interrupt program flow, but due to the RPC server's necessary use of multiprocessing, multiple signal handlers needed to be SIG_IGN'd, and further, because libtvm.so is intentionally frontend-agnostic, it's difficult to include signal handling directly in that binary (Python expects you to call PyErr_CheckSignals, but we don't require and don't want to require python-dev to compile libtvm.so, and this is the only such case where libtvm.so is expected to block the main thread for a long period of time). * Here we implement a separate microTVM debug shell python script using the non-blocking server implementation. * Add serial transport, parameterize test_zephyr to work on real hardware * add pytest test fixture, missed from previous change. * this test fixture helps to parameterize the test case * address leandron@ comment from #6703
153 lines
5.3 KiB
Python
153 lines
5.3 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
# pylint: disable=redefined-outer-name, invalid-name
|
|
"""Start an RPC server intended for use as a microTVM debugger.
|
|
|
|
microTVM aims to be runtime-agnostic, and to that end, frameworks often define command-line tools
|
|
used to launch a debug flow. These tools often manage the process of connecting to an attached
|
|
device using a hardware debugger, exposing a GDB server, and launching GDB connected to that
|
|
server with a source file attached. It's also true that this debugger can typically not be executed
|
|
concurrently with any flash tool, so this integration point is provided to allow TVM to launch and
|
|
terminate any debuggers integrated with the larger microTVM compilation/autotuning flow.
|
|
|
|
To use this tool, first launch this script in a separate terminal window. Then, provide the hostport
|
|
to your compiler's Flasher instance.
|
|
"""
|
|
|
|
import argparse
|
|
import logging
|
|
import socket
|
|
import struct
|
|
|
|
import tvm.micro.debugger as _ # NOTE: imported to expose global PackedFuncs over RPC.
|
|
|
|
from .._ffi.base import py_str
|
|
from ..rpc import base
|
|
from ..rpc import _ffi_api
|
|
|
|
|
|
_LOG = logging.getLogger(__name__)
|
|
|
|
|
|
def parse_args():
|
|
"""Parse command line arguments to this script."""
|
|
parser = argparse.ArgumentParser(description="microTVM debug-tool runner")
|
|
parser.add_argument("--host", default="0.0.0.0", help="hostname to listen on")
|
|
parser.add_argument("--port", type=int, default=9090, help="hostname to listen on")
|
|
parser.add_argument(
|
|
"--impl",
|
|
help=(
|
|
"If given, name of a module underneath tvm.micro.contrib "
|
|
"which contains the Debugger implementation to use. For example, to enable a "
|
|
"debugger named BarDebugger in python/tvm/micro/contrib/foo.py, specify either "
|
|
"'tvm.micro.contrib.foo' or 'foo' here. To enable a debugger named BazDebugger in "
|
|
"a third-party module ext_package.debugger, specify 'ext_package.debugger' here. "
|
|
"NOTE: the module cannot be in a sub-package of tvm.micro.contrib."
|
|
),
|
|
)
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
class ConnectionClosedError(Exception):
|
|
"""Raised when the connection is closed."""
|
|
|
|
|
|
def handle_conn(conn, rpc_key):
|
|
"""Handle a single connection that has just been accept'd()."""
|
|
|
|
def send(data):
|
|
conn.sendall(data)
|
|
return len(data)
|
|
|
|
magic = struct.unpack("<i", base.recvall(conn, 4))[0]
|
|
if magic != base.RPC_MAGIC:
|
|
conn.close()
|
|
return
|
|
|
|
keylen = struct.unpack("<i", base.recvall(conn, 4))[0]
|
|
key = py_str(base.recvall(conn, keylen))
|
|
arr = key.split()
|
|
expect_header = "client:"
|
|
server_key = "server:" + rpc_key
|
|
if arr[0] != expect_header:
|
|
conn.sendall(struct.pack("<i", base.RPC_CODE_MISMATCH))
|
|
_LOG.warning("mismatch key from %s", addr)
|
|
return
|
|
|
|
conn.sendall(struct.pack("<i", base.RPC_CODE_SUCCESS))
|
|
conn.sendall(struct.pack("<i", len(server_key)))
|
|
conn.sendall(server_key.encode("utf-8"))
|
|
server = _ffi_api.CreateEventDrivenServer(send, "microtvm-rpc-debugger", key)
|
|
|
|
def _readall(n):
|
|
buf = bytearray()
|
|
while len(buf) < n:
|
|
x = conn.recv(n - len(buf))
|
|
if not x:
|
|
raise ConnectionClosedError()
|
|
|
|
buf = buf + x
|
|
|
|
return buf
|
|
|
|
while True:
|
|
packet_length_bytes = _readall(8)
|
|
packet_length = struct.unpack("<q", packet_length_bytes)[0]
|
|
if not packet_length:
|
|
break
|
|
|
|
status = server(packet_length_bytes, 3)
|
|
if status == 0:
|
|
break
|
|
|
|
packet_body = _readall(packet_length)
|
|
status = server(packet_body, 3)
|
|
|
|
|
|
def main():
|
|
"""Main entry point for microTVM debug shell."""
|
|
args = parse_args()
|
|
logging.basicConfig(level=logging.INFO)
|
|
if args.impl:
|
|
package = None
|
|
if "." not in args.impl:
|
|
package = f"tvm.micro.contrib.{args.impl}"
|
|
importlib.import_module(args.impl, package)
|
|
|
|
sock = socket.socket(base.get_addr_family([args.host, args.port]), socket.SOCK_STREAM)
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
sock.bind((args.host, args.port))
|
|
sock.listen(1)
|
|
bind_addr, bind_port = sock.getsockname()
|
|
_LOG.info("listening for connections on %s:%d", bind_addr, bind_port)
|
|
|
|
while True:
|
|
conn, peer = sock.accept()
|
|
_LOG.info("accepted connection from %s", peer)
|
|
try:
|
|
handle_conn(conn, "")
|
|
except ConnectionClosedError:
|
|
pass
|
|
finally:
|
|
conn.close()
|
|
_LOG.info("closed connection from %s", peer)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|