From fd2ee49b2169c318627225bf74c645f7a3b71900 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 31 Aug 2020 13:00:14 -0700 Subject: [PATCH] ci/bare-metal: Use python for handling fastboot booting and parsing Modeling after what I did for cros_servo_run.py, this gives us easy support for restarting the test run a530 when we detect a spontaneous reboot. I had to touch up serial_buffer.py to handle buffering in from a file instead of a serial device, to support the upcoming etnaviv CI (tested by running it against a serial log from db410c and seeing it step to calling "fastboot") Part-of: --- .gitlab-ci/bare-metal/fastboot.sh | 47 +++--------- .gitlab-ci/bare-metal/fastboot_run.py | 102 +++++++++++++++++++++++++ .gitlab-ci/bare-metal/serial_buffer.py | 42 ++++++++-- 3 files changed, 148 insertions(+), 43 deletions(-) create mode 100755 .gitlab-ci/bare-metal/fastboot_run.py diff --git a/.gitlab-ci/bare-metal/fastboot.sh b/.gitlab-ci/bare-metal/fastboot.sh index 81d94766cdc..3a6d1f1550b 100755 --- a/.gitlab-ci/bare-metal/fastboot.sh +++ b/.gitlab-ci/bare-metal/fastboot.sh @@ -57,7 +57,6 @@ set -ex # Clear out any previous run's artifacts. rm -rf results/ mkdir -p results -find artifacts/ -name serial\*.txt | xargs rm -f # Create the rootfs in a temp dir rsync -a --delete $BM_ROOTFS/ rootfs/ @@ -90,41 +89,19 @@ if [ -n "$WEBDAV_CMDLINE" ]; then nginx fi -# Start watching serial, and power up the device. -if [ -n "$BM_SERIAL" ]; then - python3 $BM/serial_buffer.py \ - --dev $BM_SERIAL \ - --file artifacts/serial-output.txt \ - --prefix "SERIAL> " & -else - PATH=$BM:$PATH $BM_SERIAL_SCRIPT | tee artifacts/serial-output.txt & -fi - -while [ ! -e artifacts/serial-output.txt ]; do - sleep 1 -done -PATH=$BM:$PATH $BM_POWERUP - -# Once fastboot is ready, boot our image. -$BM/expect-output.sh artifacts/serial-output.txt \ - -f "fastboot: processing commands" \ - -f "Listening for fastboot command on" \ - -e "data abort" +export PATH=$BM:$PATH -fastboot boot -s $BM_FASTBOOT_SERIAL artifacts/fastboot.img +# Start background command for talking to serial if we have one. +if [ -n "$BM_SERIAL_SCRIPT" ]; then + $BM_SERIAL_SCRIPT | tee results/serial-output.txt & -# Wait for the device to complete the deqp run -$BM/expect-output.sh artifacts/serial-output.txt \ - -f "bare-metal result" \ - -e "---. end Kernel panic" - -# power down the device -PATH=$BM:$PATH $BM_POWERDOWN - -set +e -if grep -q "bare-metal result: pass" artifacts/serial-output.txt; then - exit 0 -else - exit 1 + while [ ! -e results/serial-output.txt ]; do + sleep 1 + done fi +$BM/fastboot_run.py \ + --dev="$BM_SERIAL" \ + --fbserial="$BM_FASTBOOT_SERIAL" \ + --powerup="$BM_POWERUP" \ + --powerdown="$BM_POWERDOWN" diff --git a/.gitlab-ci/bare-metal/fastboot_run.py b/.gitlab-ci/bare-metal/fastboot_run.py new file mode 100755 index 00000000000..6f286539488 --- /dev/null +++ b/.gitlab-ci/bare-metal/fastboot_run.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright © 2020 Google LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import argparse +import os +import re +from serial_buffer import SerialBuffer +import sys +import threading + +class FastbootRun: + def __init__(self, args): + self.powerup = args.powerup + self.ser = SerialBuffer(args.dev, "results/serial-output.txt", "R SERIAL> ") + self.fastboot="fastboot boot -s {ser} artifacts/fastboot.img".format(ser=args.fbserial) + + def logged_system(self, cmd): + print("Running '{}'".format(cmd)) + return os.system(cmd) + + def run(self): + if self.logged_system(self.powerup) != 0: + return 1 + + fastboot_ready = False + for line in self.ser.lines(): + if re.search("fastboot: processing commands", line) or \ + re.search("Listening for fastboot command on", line): + fastboot_ready = True + break + + if re.search("data abort", line): + return 1 + + if not fastboot_ready: + print("Failed to get to fastboot prompt") + return 1 + + if self.logged_system(self.fastboot) != 0: + return 1 + + for line in self.ser.lines(): + if re.search("---. end Kernel panic", line): + return 1 + + # The db820c boards intermittently reboot. Just restart the run + # when if we see a reboot after we got past fastboot. + if re.search("PON REASON", line): + print("Detected spontaneous reboot, restarting run...") + return 2 + + result = re.search("bare-metal result: (\S*)", line) + if result: + if result.group(1) == "pass": + return 0 + else: + return 1 + + print("Reached the end of the CPU serial log without finding a result") + return 1 + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--dev', type=str, help='Serial device (otherwise reading from serial-output.txt)') + parser.add_argument('--powerup', type=str, help='shell command for rebooting', required=True) + parser.add_argument('--powerdown', type=str, help='shell command for powering off', required=True) + parser.add_argument('--fbserial', type=str, help='fastboot serial number of the board', required=True) + args = parser.parse_args() + + fastboot = FastbootRun(args) + + while True: + retval = fastboot.run() + if retval != 2: + break + + fastboot.logged_system(args.powerdown) + + sys.exit(retval) + +if __name__ == '__main__': + main() diff --git a/.gitlab-ci/bare-metal/serial_buffer.py b/.gitlab-ci/bare-metal/serial_buffer.py index 886bce64d14..d461721164a 100755 --- a/.gitlab-ci/bare-metal/serial_buffer.py +++ b/.gitlab-ci/bare-metal/serial_buffer.py @@ -26,19 +26,30 @@ from datetime import datetime,timezone import queue import serial import threading +import time class SerialBuffer: def __init__(self, dev, filename, prefix): - self.f = open(filename, "wb+") + self.filename = filename self.dev = dev - self.serial = serial.Serial(dev, 115200, timeout=10) + + if dev: + self.f = open(filename, "wb+") + self.serial = serial.Serial(dev, 115200, timeout=10) + else: + self.f = open(filename, "rb") + self.byte_queue = queue.Queue() self.line_queue = queue.Queue() self.prefix = prefix self.sentinel = object() - self.read_thread = threading.Thread(target=self.serial_read_thread_loop, daemon=True) + if self.dev: + self.read_thread = threading.Thread(target=self.serial_read_thread_loop, daemon=True) + else: + self.read_thread = threading.Thread(target=self.serial_file_read_thread_loop, daemon=True) self.read_thread.start() + self.lines_thread = threading.Thread(target=self.serial_lines_thread_loop, daemon=True) self.lines_thread.start() @@ -56,6 +67,19 @@ class SerialBuffer: self.byte_queue.put(self.sentinel) break + # Thread that just reads the bytes from the file of serial output that some + # other process is appending to. + def serial_file_read_thread_loop(self): + greet = "Serial thread reading from %s\n" % self.filename + self.byte_queue.put(greet.encode()) + + while True: + line = self.f.readline() + if line: + self.byte_queue.put(line) + else: + time.sleep(0.1) + # Thread that processes the stream of bytes to 1) log to stdout, 2) log to # file, 3) add to the queue of lines to be read by program logic @@ -69,9 +93,11 @@ class SerialBuffer: self.line_queue.put(self.sentinel) break; - # Write our data to the output file - self.f.write(bytes) - self.f.flush() + # Write our data to the output file if we're the ones reading from + # the serial device + if self.dev: + self.f.write(bytes) + self.f.flush() for b in bytes: line.append(b) @@ -96,8 +122,8 @@ class SerialBuffer: def main(): parser = argparse.ArgumentParser() - parser.add_argument('--dev', type=str, help='Serial device', required=True) - parser.add_argument('--file', type=str, help='Filename to output our serial data to') + parser.add_argument('--dev', type=str, help='Serial device') + parser.add_argument('--file', type=str, help='Filename for serial output', required=True) parser.add_argument('--prefix', type=str, help='Prefix for logging serial to stdout', nargs='?') args = parser.parse_args() -- 2.30.2