From 6abe6067da36650deb6f64070dcd2b5cd072469b Mon Sep 17 00:00:00 2001 From: Sascha Bischoff Date: Tue, 12 Jun 2018 16:24:39 +0100 Subject: [PATCH] trace/serial_trace: Add capability to trace serial traffic We add a TraceCollector which logs the traffic on a serial port. This can then be used to debug why a board crashes, or to extract extra information from the device whilst it is running a workload. --- devlib/__init__.py | 1 + devlib/trace/serial_trace.py | 75 ++++++++++++++++++++++++++++++++++++ devlib/utils/serial_port.py | 4 +- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 devlib/trace/serial_trace.py diff --git a/devlib/__init__.py b/devlib/__init__.py index 110f5aa..af8a336 100644 --- a/devlib/__init__.py +++ b/devlib/__init__.py @@ -25,6 +25,7 @@ from devlib.derived.energy import DerivedEnergyMeasurements from devlib.derived.fps import DerivedGfxInfoStats, DerivedSurfaceFlingerStats from devlib.trace.ftrace import FtraceCollector +from devlib.trace.serial_trace import SerialTraceCollector from devlib.host import LocalConnection from devlib.utils.android import AdbConnection diff --git a/devlib/trace/serial_trace.py b/devlib/trace/serial_trace.py new file mode 100644 index 0000000..e77ad9d --- /dev/null +++ b/devlib/trace/serial_trace.py @@ -0,0 +1,75 @@ +from pexpect.exceptions import TIMEOUT +import shutil +from tempfile import NamedTemporaryFile + +from devlib.trace import TraceCollector +from devlib.utils.serial_port import get_connection + + +class SerialTraceCollector(TraceCollector): + + @property + def collecting(self): + return self._collecting + + def __init__(self, target, serial_port, baudrate, timeout=20): + super(SerialTraceCollector, self).__init__(target) + self.serial_port = serial_port + self.baudrate = baudrate + self.timeout = timeout + + self._serial_target = None + self._conn = None + self._tmpfile = None + self._collecting = False + + def reset(self): + if self._collecting: + raise RuntimeError("reset was called whilst collecting") + + if self._tmpfile: + self._tmpfile.close() + self._tmpfile = None + + def start(self): + if self._collecting: + raise RuntimeError("start was called whilst collecting") + + + self._tmpfile = NamedTemporaryFile() + self._tmpfile.write("-------- Starting serial logging --------\n") + + self._serial_target, self._conn = get_connection(port=self.serial_port, + baudrate=self.baudrate, + timeout=self.timeout, + logfile=self._tmpfile, + init_dtr=0) + self._collecting = True + + def stop(self): + if not self._collecting: + raise RuntimeError("stop was called whilst not collecting") + + # We expect the below to fail, but we need to get pexpect to + # do something so that it interacts with the serial device, + # and hence updates the logfile. + try: + self._serial_target.expect(".", timeout=1) + except TIMEOUT: + pass + + self._serial_target.close() + del self._conn + + self._tmpfile.write("-------- Stopping serial logging --------\n") + + self._collecting = False + + def get_trace(self, outfile): + if self._collecting: + raise RuntimeError("get_trace was called whilst collecting") + + shutil.copy(self._tmpfile.name, outfile) + + self._tmpfile.close() + self._tmpfile = None diff --git a/devlib/utils/serial_port.py b/devlib/utils/serial_port.py index 2035124..ba416dc 100644 --- a/devlib/utils/serial_port.py +++ b/devlib/utils/serial_port.py @@ -49,7 +49,7 @@ def pulse_dtr(conn, state=True, duration=0.1): def get_connection(timeout, init_dtr=None, logcls=SerialLogger, - *args, **kwargs): + logfile=None, *args, **kwargs): if init_dtr is not None: kwargs['dsrdtr'] = True try: @@ -60,7 +60,7 @@ def get_connection(timeout, init_dtr=None, logcls=SerialLogger, conn.setDTR(init_dtr) conn.nonblocking() conn.flushOutput() - target = fdpexpect.fdspawn(conn.fileno(), timeout=timeout) + target = fdpexpect.fdspawn(conn.fileno(), timeout=timeout, logfile=logfile) target.logfile_read = logcls('read') target.logfile_send = logcls('send')