diff options
-rwxr-xr-x | debug/rbb_daisychain.py | 10 | ||||
-rw-r--r-- | debug/targets.py | 41 | ||||
-rw-r--r-- | debug/testlib.py | 313 |
3 files changed, 191 insertions, 173 deletions
diff --git a/debug/rbb_daisychain.py b/debug/rbb_daisychain.py index b7c21e6..4d9ea8a 100755 --- a/debug/rbb_daisychain.py +++ b/debug/rbb_daisychain.py @@ -56,8 +56,8 @@ class Chain: assert len(values) == 0 values = list(tap.execute(bytes(tmp_commands))) if self.debug: - sys.stdout.write(" %d %r -> %r\n" % (i, bytes(tmp_commands), - bytes(values))) + sys.stdout.write(f" {i} {bytes(tmp_commands)!r} -> " + f"{bytes(values)!r}\n") return bytes(values) def main(): @@ -80,7 +80,7 @@ def main(): for port in args.tap_port: chain.append(Tap(port)) - sys.stdout.write("Listening on port %d.\n" % server.getsockname()[1]) + sys.stdout.write(f"Listening on port {server.getsockname()[1]}.\n") sys.stdout.flush() while True: @@ -98,10 +98,10 @@ def main(): break if args.debug: - sys.stdout.write("%r\n" % commands) + sys.stdout.write(f"{commands!r}\n") result = chain.execute(commands) if args.debug: - sys.stdout.write(" -> %r\n" % result) + sys.stdout.write(f" -> {result!r}\n") client.send(result) client.close() diff --git a/debug/targets.py b/debug/targets.py index 727a882..504a550 100644 --- a/debug/targets.py +++ b/debug/targets.py @@ -143,7 +143,7 @@ class Target: self.name = type(self).__name__ # Default OpenOCD config file to <name>.cfg if not self.openocd_config_path: - self.openocd_config_path = "%s.cfg" % self.name + self.openocd_config_path = f"{self.name}.cfg" self.openocd_config_path = os.path.join(self.directory, self.openocd_config_path) for i, hart in enumerate(self.harts): @@ -151,10 +151,10 @@ class Target: if not hasattr(hart, 'id'): hart.id = i if not hart.name: - hart.name = "%s-%d" % (self.name, i) + hart.name = f"{self.name}-{i}" # Default link script to <name>.lds if not hart.link_script_path: - hart.link_script_path = "%s.lds" % self.name + hart.link_script_path = f"{self.name}.lds" hart.link_script_path = os.path.join(self.directory, hart.link_script_path) @@ -169,11 +169,12 @@ class Target: freertos=test.freertos()) def do_compile(self, hart, *sources): - binary_name = "%s_%s-%x" % ( - self.name, - os.path.basename(os.path.splitext(sources[0])[0]), - hart.misa) + binary_name = ( + self.name + "_" + + os.path.basename(os.path.splitext(sources[0])[0]) + "-" + + f"{hart.misa:x}") if Target.isolate: + # pylint: disable-next=consider-using-with self.temporary_binary = tempfile.NamedTemporaryFile( prefix=binary_name + "_") binary_name = self.temporary_binary.name @@ -181,12 +182,12 @@ class Target: args = list(sources) + [ "programs/entry.S", "programs/init.c", - "-DNHARTS=%d" % len(self.harts), + f"-DNHARTS={len(self.harts)}", "-I", "../env", "-T", hart.link_script_path, "-nostartfiles", "-mcmodel=medany", - "-DXLEN=%d" % hart.xlen, + f"-DXLEN={hart.xlen}", "-o", binary_name] if hart.extensionSupported('e'): @@ -194,17 +195,17 @@ class Target: args.append("-mabi=ilp32e") args.append("-DRV32E") else: - march = "rv%dima" % hart.xlen + march = f"rv{hart.xlen}ima" for letter in "fdc": if hart.extensionSupported(letter): march += letter if hart.extensionSupported("v") and self.compiler_supports_v: march += "v" - args.append("-march=%s" % march) + args.append(f"-march={march}") if hart.xlen == 32: args.append("-mabi=ilp32") else: - args.append("-mabi=lp%d" % hart.xlen) + args.append(f"-mabi=lp{hart.xlen}") testlib.compile(args) return binary_name @@ -221,8 +222,8 @@ class Target: r"ISA extension `(\w)'", e.stderr.decode()) if m and m.group(1) in "v": extension = m.group(1) - print("Disabling extension %r because the " - "compiler doesn't support it." % extension) + print(f"Disabling extension {extension!r} because the " + "compiler doesn't support it.") self.compiler_supports_v = False else: raise @@ -260,18 +261,18 @@ def target(parsed): definition = getattr(module, name) if isinstance(definition, type) and issubclass(definition, Target): found.append(definition) - assert len(found) == 1, "%s does not define exactly one subclass of " \ - "targets.Target" % parsed.target + assert len(found) == 1, (f"{parsed.target} does not define exactly one " + "subclass of targets.Target") t = found[0](parsed.target, parsed) - assert t.harts, "%s doesn't have any harts defined!" % t.name + assert t.harts, f"{t.name} doesn't have any harts defined!" if parsed.xlen > 0: for h in t.harts: if h.xlen == 0: h.xlen = parsed.xlen elif h.xlen != parsed.xlen: - raise Exception("The target hart specified an XLEN of %d, but "\ - "the command line specified an XLEN of %d. They must "\ - "match." % (h.xlen, parsed.xlen)) + raise Exception("The target hart specified an XLEN of " + f"{h.xlen}, but the command line specified an XLEN of " + f"{parsed.xlen}. They must match.") return t diff --git a/debug/testlib.py b/debug/testlib.py index 6b4854f..cfb31a3 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -50,14 +50,14 @@ def compile(args): # pylint: disable=redefined-builtin cmd.append(arg) header("Compile") print("+", " ".join(cmd)) - process = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - if process.returncode: - print(stdout.decode('ascii'), end=" ") - print(stderr.decode('ascii'), end=" ") - header("") - raise CompileError(stdout, stderr) + with subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) as process: + stdout, stderr = process.communicate() + if process.returncode: + print(stdout.decode('ascii'), end=" ") + print(stderr.decode('ascii'), end=" ") + header("") + raise CompileError(stdout, stderr) class Spike: # pylint: disable=too-many-instance-attributes @@ -86,22 +86,25 @@ class Spike: "programs/checksum.c", "programs/tiny-malloc.c", "programs/infinite_loop.S", "-DDEFINE_MALLOC", "-DDEFINE_FREE") cmd.append(self.infinite_loop) + # pylint: disable-next=consider-using-with self.logfile = tempfile.NamedTemporaryFile(prefix="spike-", suffix=".log") logname = self.logfile.name self.lognames = [logname] if print_log_names: - real_stdout.write("Temporary spike log: %s\n" % logname) - self.logfile.write(("+ %s\n" % " ".join(cmd)).encode()) + real_stdout.write("Temporary spike log: {logname}\n") + self.logfile.write(("+ " + " ".join(cmd) + "\n").encode()) self.logfile.flush() + # pylint: disable-next=consider-using-with self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=self.logfile, stderr=self.logfile) if with_jtag_gdb: self.port = None for _ in range(30): - m = re.search(r"Listening for remote bitbang connection on " - r"port (\d+).", open(logname).read()) + with open(logname, encoding='utf-8') as fd: + m = re.search(r"Listening for remote bitbang connection on " + r"port (\d+).", fd.read()) if m: self.port = int(m.group(1)) os.environ['REMOTE_BITBANG_PORT'] = m.group(1) @@ -120,7 +123,7 @@ class Spike: else: cmd = ["spike"] - cmd += ["-p%d" % len(self.harts)] + cmd += [f"-p{len(self.harts)}"] assert len(set(t.xlen for t in self.harts)) == 1, \ "All spike harts must have the same XLEN" @@ -128,7 +131,7 @@ class Spike: if self.isa: isa = self.isa else: - isa = "RV%dG" % self.harts[0].xlen + isa = f"RV{self.harts[0].xlen}G" cmd += ["--isa", isa] cmd += ["--dm-auth"] @@ -153,14 +156,14 @@ class Spike: cmd.append("--dm-no-halt-groups") if 'V' in isa[2:]: - cmd.append("--varch=vlen:%d,elen:%d" % (self.vlen, self.elen)) + cmd.append(f"--varch=vlen:{self.vlen},elen:{self.elen}") assert len(set(t.ram for t in self.harts)) == 1, \ "All spike harts must have the same RAM layout" assert len(set(t.ram_size for t in self.harts)) == 1, \ "All spike harts must have the same RAM layout" - os.environ['WORK_AREA'] = '0x%x' % self.harts[0].ram - cmd += ["-m0x%x:0x%x" % (self.harts[0].ram, self.harts[0].ram_size)] + os.environ['WORK_AREA'] = f'0x{self.harts[0].ram:x}' + cmd += [f"-m0x{self.harts[0].ram:x}:0x{self.harts[0].ram_size:x}"] if timeout: cmd = ["timeout", str(timeout)] + cmd @@ -190,6 +193,7 @@ class MultiSpike: self.spikes = spikes self.lognames = sum((spike.lognames for spike in spikes), []) + # pylint: disable-next=consider-using-with self.logfile = tempfile.NamedTemporaryFile(prefix="daisychain-", suffix=".log") self.lognames.append(self.logfile.name) @@ -197,15 +201,16 @@ class MultiSpike: # Now create the daisy-chain process. cmd = ["./rbb_daisychain.py", "0"] + \ [str(spike.port) for spike in spikes] - self.logfile.write(("+ %s\n" % cmd).encode()) + self.logfile.write(f"+ {cmd}\n".encode()) self.logfile.flush() + # pylint: disable-next=consider-using-with self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=self.logfile, stderr=self.logfile) self.port = None for _ in range(30): - m = re.search(r"Listening on port (\d+).", - open(self.lognames[-1]).read()) + with open(self.lognames[-1], encoding='utf=8') as fd: + m = re.search(r"Listening on port (\d+).", fd.read()) if m: self.port = int(m.group(1)) break @@ -227,6 +232,7 @@ class MultiSpike: pass class VcsSim: + # pylint: disable-next=consider-using-with logfile = tempfile.NamedTemporaryFile(prefix='simv', suffix='.log') logname = logfile.name @@ -240,37 +246,39 @@ class VcsSim: cmd[0] = cmd[0] + "-debug" cmd += ["+vcdplusfile=output/gdbserver.vpd"] - logfile = open(self.logname, "w") + # pylint: disable-next=consider-using-with + logfile = open(self.logname, "w", encoding='utf-8') if print_log_names: - real_stdout.write("Temporary VCS log: %s\n" % self.logname) - logfile.write("+ %s\n" % " ".join(cmd)) + real_stdout.write(f"Temporary VCS log: {self.logname}\n") + logfile.write("+ " + " ".join(cmd) + "\n") logfile.flush() - listenfile = open(self.logname, "r") - listenfile.seek(0, 2) - self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, - stdout=logfile, stderr=logfile) - done = False - start = time.time() - while not done: - # Fail if VCS exits early - exit_code = self.process.poll() - if exit_code is not None: - raise RuntimeError('VCS simulator exited early with status %d' - % exit_code) - - line = listenfile.readline() - if not line: - time.sleep(1) - match = re.match(r"^Listening on port (\d+)$", line) - if match: - done = True - self.port = int(match.group(1)) - os.environ['JTAG_VPI_PORT'] = str(self.port) - - if (time.time() - start) > timeout: - raise Exception("Timed out waiting for VCS to listen for JTAG " - "vpi") + with open(self.logname, "r", encoding='utf-8') as listenfile: + listenfile.seek(0, 2) + # pylint: disable-next=consider-using-with + self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, + stdout=logfile, stderr=logfile) + done = False + start = time.time() + while not done: + # Fail if VCS exits early + exit_code = self.process.poll() + if exit_code is not None: + raise RuntimeError( + f'VCS simulator exited early with status {exit_code}') + + line = listenfile.readline() + if not line: + time.sleep(1) + match = re.match(r"^Listening on port (\d+)$", line) + if match: + done = True + self.port = int(match.group(1)) + os.environ['JTAG_VPI_PORT'] = str(self.port) + + if (time.time() - start) > timeout: + raise Exception( + "Timed out waiting for VCS to listen for JTAG vpi") def __del__(self): try: @@ -280,6 +288,7 @@ class VcsSim: pass class Openocd: + # pylint: disable-next=consider-using-with logfile = tempfile.NamedTemporaryFile(prefix='openocd', suffix='.log') logname = logfile.name @@ -325,24 +334,26 @@ class Openocd: else: extra_env['USE_FREERTOS'] = "0" + # pylint: disable-next=consider-using-with raw_logfile = open(Openocd.logname, "wb") try: + # pylint: disable-next=consider-using-with spike_dasm = subprocess.Popen("spike-dasm", stdin=subprocess.PIPE, stdout=raw_logfile, stderr=raw_logfile) logfile = spike_dasm.stdin except FileNotFoundError: logfile = raw_logfile if print_log_names: - real_stdout.write("Temporary OpenOCD log: %s\n" % Openocd.logname) + real_stdout.write(f"Temporary OpenOCD log: {Openocd.logname}\n") env_entries = ("REMOTE_BITBANG_HOST", "REMOTE_BITBANG_PORT", "WORK_AREA") env_entries = [key for key in env_entries if key in os.environ] parts = [ - " ".join("%s=%s" % (key, os.environ[key]) for key in env_entries), - " ".join("%s=%s" % (k, v) for k, v in extra_env.items()), + " ".join(f"{key}={os.environ[key]}" for key in env_entries), + " ".join(f"{k}={v}" for k, v in extra_env.items()), " ".join(map(pipes.quote, cmd)) ] - logfile.write(("+ %s\n" % " ".join(parts)).encode()) + logfile.write(("+ " + " ".join(parts) + "\n").encode()) logfile.flush() self.gdb_ports = [] @@ -350,6 +361,7 @@ class Openocd: def start(self, cmd, logfile, extra_env): combined_env = {**os.environ, **extra_env} + # pylint: disable-next=consider-using-with process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=logfile, stderr=logfile, env=combined_env) @@ -360,29 +372,29 @@ class Openocd: # attempt too early. start = time.time() messaged = False - fd = open(Openocd.logname, "r") - while True: - line = fd.readline() - if not line: - if not process.poll() is None: - raise Exception("OpenOCD exited early.") - time.sleep(0.1) - continue - - m = re.search(r"Listening on port (\d+) for gdb connections", - line) - if m: - self.gdb_ports.append(int(m.group(1))) - - if "telnet server disabled" in line: - return process - - if not messaged and time.time() - start > 1: - messaged = True - print("Waiting for OpenOCD to start...") - if (time.time() - start) > self.timeout: - raise Exception("Timed out waiting for OpenOCD to " - "listen for gdb") + with open(Openocd.logname, "r", encoding='utf-8') as fd: + while True: + line = fd.readline() + if not line: + if not process.poll() is None: + raise Exception("OpenOCD exited early.") + time.sleep(0.1) + continue + + m = re.search( + r"Listening on port (\d+) for gdb connections", line) + if m: + self.gdb_ports.append(int(m.group(1))) + + if "telnet server disabled" in line: + return process + + if not messaged and time.time() - start > 1: + messaged = True + print("Waiting for OpenOCD to start...") + if (time.time() - start) > self.timeout: + raise Exception("Timed out waiting for OpenOCD to " + "listen for gdb") except Exception: print_log(Openocd.logname) @@ -403,15 +415,16 @@ class Openocd: def smp(self): """Return true iff OpenOCD internally sees the harts as part of an SMP group.""" - for line in open(self.config_file, "r"): - if "target smp" in line: - return True + with open(self.config_file, "r", encoding='utf-8') as handle: + for line in handle: + if "target smp" in line: + return True return False class OpenocdCli: def __init__(self, port=4444): self.child = pexpect.spawn( - "sh -c 'telnet localhost %d | tee openocd-cli.log'" % port) + f"sh -c 'telnet localhost {port} | tee openocd-cli.log'") self.child.expect("> ") def command(self, cmd): @@ -422,7 +435,7 @@ class OpenocdCli: return self.child.before.strip("\t\r\n \0").decode("utf-8") def reg(self, reg=''): - output = self.command("reg %s" % reg) + output = self.command(f"reg {reg}") matches = re.findall(r"(\w+) \(/\d+\): (0x[0-9A-F]+)", output) values = {r: int(v, 0) for r, v in matches} if reg: @@ -430,7 +443,7 @@ class OpenocdCli: return values def load_image(self, image): - output = self.command("load_image %s" % image) + output = self.command(f"load_image {image}") if 'invalid ELF file, only 32bits files are supported' in output: raise TestNotApplicable(output) @@ -456,7 +469,7 @@ class NoSymbol(Exception): self.symbol = symbol def __repr__(self): - return "NoSymbol(%r)" % self.symbol + return f"NoSymbol({self.symbol!r})" Thread = collections.namedtuple('Thread', ('id', 'description', 'target_id', 'name', 'frame')) @@ -541,13 +554,13 @@ def parse_tokens(tokens): return parse_dict_or_list(tokens) if isinstance(tokens[0], str): return tokens.pop(0) - raise Exception("Unsupported tokens: %r" % tokens) + raise Exception(f"Unsupported tokens: {tokens!r}") def parse_rhs(text): tokens = list(tokenize(text)) result = parse_tokens(tokens) if tokens: - raise Exception("Unexpected input: %r" % tokens) + raise Exception(f"Unexpected input: {tokens!r}") return result class Gdb: @@ -576,14 +589,15 @@ class Gdb: self.logfiles = [] self.children = [] for port in ports: - logfile = tempfile.NamedTemporaryFile(prefix="gdb@%d-" % port, - suffix=".log") + # pylint: disable-next=consider-using-with + logfile = tempfile.NamedTemporaryFile(prefix=f"gdb@{port}-", + suffix=".log") self.logfiles.append(logfile) if print_log_names: - real_stdout.write("Temporary gdb log: %s\n" % logfile.name) + real_stdout.write(f"Temporary gdb log: {logfile.name}\n") child = pexpect.spawn(self.cmd) child.logfile = logfile - child.logfile.write(("+ %s\n" % self.cmd).encode()) + child.logfile.write(f"+ {self.cmd}\n".encode()) self.children.append(child) self.select_child(child) self.wait() @@ -593,9 +607,8 @@ class Gdb: self.command("set height 0", reset_delays=None) # Force consistency. self.command("set print entry-values no", reset_delays=None) - self.command("set remotetimeout %d" % self.timeout, - reset_delays=None) - self.command("set remotetimeout %d" % self.target.timeout_sec) + self.command(f"set remotetimeout {self.timeout}", reset_delays=None) + self.command(f"set remotetimeout {self.target.timeout_sec}") self.active_child = self.children[0] def connect(self): @@ -603,10 +616,10 @@ class Gdb: for port, child, binary in zip(self.ports, self.children, self.binaries): self.select_child(child) - self.command("target extended-remote localhost:%d" % port, + self.command(f"target extended-remote localhost:{port}", ops=10, reset_delays=None) if binary: - output = self.command("file %s" % binary) + output = self.command(f"file {binary}") assertIn("Reading symbols", output) threads = self.threads() for t in threads: @@ -648,7 +661,7 @@ class Gdb: h = self.harts[hart.id] self.select_child(h['child']) if not h['solo']: - output = self.command("thread %s" % h['thread'].id, ops=5) + output = self.command(f"thread {h['thread'].id}", ops=5) assert "Unknown" not in output def push_state(self): @@ -673,7 +686,7 @@ class Gdb: reset_delays = self.reset_delays[self.reset_delay_index] self.reset_delay_index = (self.reset_delay_index + 1) % \ len(self.reset_delays) - self.command("monitor riscv reset_delays %d" % reset_delays, + self.command(f"monitor riscv reset_delays {reset_delays}", reset_delays=None) timeout = max(1, ops) * self.timeout self.active_child.sendline(command) @@ -732,13 +745,13 @@ class Gdb: else: sync = "&" if wait: - output = self.command("c%s" % sync, ops=ops) + output = self.command(f"c{sync}", ops=ops) if checkOutput: assert "Continuing" in output assert "Could not insert hardware" not in output return output else: - self.active_child.sendline("c%s" % sync) + self.active_child.sendline(f"c{sync}") self.active_child.expect("Continuing", timeout=ops * self.timeout) return "" @@ -774,8 +787,7 @@ class Gdb: self.interrupt() def x(self, address, size='w', count=1): - output = self.command("x/%d%s %s" % (count, size, address), - ops=count / 16) + output = self.command(f"x/{count}{size} {address}", ops=count / 16) values = [] for line in output.splitlines(): for value in line.split(':')[1].strip().split(): @@ -785,14 +797,14 @@ class Gdb: return values def p_raw(self, obj): - output = self.command("p %s" % obj) + output = self.command(f"p {obj}") m = re.search("Cannot access memory at address (0x[0-9a-f]+)", output) if m: raise CannotAccess(int(m.group(1), 0)) return output.split('=', 1)[-1].strip() def p(self, obj, fmt="/x", ops=1): - output = self.command("p%s %s" % (fmt, obj), ops=ops).splitlines()[-1] + output = self.command(f"p{fmt} {obj}", ops=ops).splitlines()[-1] rhs = output.split('=', 1)[-1] return parse_rhs(rhs) @@ -803,12 +815,12 @@ class Gdb: return result def p_string(self, obj): - output = self.command("p %s" % obj) + output = self.command(f"p {obj}") value = shlex.split(output.split('=')[-1].strip())[1] return value def info_registers(self, group="", ops=5): - output = self.command("info registers %s" % group, ops=ops) + output = self.command(f"info registers {group}", ops=ops) result = {} for line in output.splitlines(): m = re.match(r"(\w+)\s+({.*})(?:\s+(\(.*\)))?", line) @@ -836,7 +848,7 @@ class Gdb: assert "MIS" not in output def b(self, location): - output = self.command("b %s" % location, ops=5) + output = self.command(f"b {location}", ops=5) assert "not defined" not in output assert "Breakpoint" in output m = re.search(r"Breakpoint (\d+),? ", output) @@ -844,13 +856,13 @@ class Gdb: return int(m.group(1)) def hbreak(self, location): - output = self.command("hbreak %s" % location, ops=5) + output = self.command(f"hbreak {location}", ops=5) assert "not defined" not in output assert "Hardware assisted breakpoint" in output return output def watch(self, expr): - output = self.command("watch %s" % expr, ops=5) + output = self.command(f"watch {expr}", ops=5) assert "not defined" not in output assert "atchpoint" in output return output @@ -858,7 +870,7 @@ class Gdb: def swatch(self, expr): self.command("show can-use-hw-watchpoints") self.command("set can-use-hw-watchpoints 0") - output = self.command("watch %s" % expr, ops=5) + output = self.command(f"watch {expr}", ops=5) assert "not defined" not in output assert "atchpoint" in output self.command("set can-use-hw-watchpoints 1") @@ -879,7 +891,7 @@ class Gdb: return threads def thread(self, thread): - return self.command("thread %s" % thread.id) + return self.command(f"thread {thread.id}") def where(self): return self.command("where 1") @@ -925,17 +937,16 @@ def run_all_tests(module, target, parsed): for hart in target.harts: if parsed.misaval: hart.misa = int(parsed.misaval, 16) - print("Using $misa from command line: 0x%x" % hart.misa) + print(f"Using $misa from command line: 0x{hart.misa:x}") elif hart.misa: - print("Using $misa from hart definition: 0x%x" % hart.misa) + print(f"Using $misa from hart definition: 0x{hart.misa:x}") elif not examine_added: todo.insert(0, ("ExamineTarget", ExamineTarget, None)) examine_added = True results, count = run_tests(parsed, target, todo) - header("ran %d tests in %.0fs" % (count, time.time() - overall_start), - dash=':') + header(f"ran {count} tests in {time.time() - overall_start:.0f}s", dash=':') return print_results(results) @@ -945,31 +956,34 @@ def run_tests(parsed, target, todo): count = 0 for name, definition, hart in todo: - log_name = os.path.join(parsed.logs, "%s-%s-%s.log" % - (time.strftime("%Y%m%d-%H%M%S"), type(target).__name__, name)) - log_fd = open(log_name, 'w') - print("[%s] Starting > %s" % (name, log_name)) + timestamp = time.strftime("%Y%m%d-%H%M%S") + log_name = os.path.join(parsed.logs, + f"{timestamp}-" + f"{type(target).__name__}-{name}.log") + # pylint: disable-next=consider-using-with + log_fd = open(log_name, 'w', encoding='utf-8') + print(f"[{name}] Starting > {log_name}") instance = definition(target, hart) sys.stdout.flush() - log_fd.write("Test: %s\n" % name) - log_fd.write("Target: %s\n" % type(target).__name__) + log_fd.write(f"Test: {name}\n") + log_fd.write(f"Target: {type(target).__name__}\n") start = time.time() global real_stdout # pylint: disable=global-statement real_stdout = sys.stdout sys.stdout = log_fd try: result = instance.run() - log_fd.write("Result: %s\n" % result) - log_fd.write("Logfile: %s\n" % log_name) - log_fd.write("Reproduce: %s %s %s\n" % (sys.argv[0], parsed.target, - name)) + log_fd.write(f"Result: {result}\n") + log_fd.write(f"Logfile: {log_name}\n") + log_fd.write(f"Reproduce: {sys.argv[0]} {parsed.target} {name}\n") finally: sys.stdout = real_stdout - log_fd.write("Time elapsed: %.2fs\n" % (time.time() - start)) + log_fd.write(f"Time elapsed: {time.time() - start:.2f}s\n") log_fd.flush() - print("[%s] %s in %.2fs" % (name, result, time.time() - start)) + print(f"[{name}] {result} in {time.time() - start:.2f}s") if result not in good_results and parsed.print_failures: - sys.stdout.write(open(log_name).read()) + with open(log_name, encoding='utf-8') as handle: + sys.stdout.write(handle.read()) sys.stdout.flush() results.setdefault(result, []).append((name, log_name)) count += 1 @@ -981,11 +995,11 @@ def run_tests(parsed, target, todo): def print_results(results): result = 0 for key, value in results.items(): - print("%d tests returned %s" % (len(value), key)) + print(f"{len(value)} tests returned {key}") if key not in good_results: result = 1 for name, log_name in value: - print(" %s > %s" % (name, log_name)) + print(f" {name} > {log_name}") return result @@ -1016,7 +1030,7 @@ def header(title, dash='-', length=78): dashes = dash * (length - 4 - len(title)) before = dashes[:len(dashes)//2] after = dashes[len(dashes)//2:] - print("%s[ %s ]%s" % (before, title, after)) + print(f"{before}[ {title} ]{after}") else: print(dash * length) @@ -1027,7 +1041,8 @@ def print_log_handle(name, handle): print() def print_log(path): - print_log_handle(path, open(path, "r", errors='ignore')) + with open(path, "r", errors='ignore', encoding='utf-8') as handle: + print_log_handle(path, handle) class BaseTest: # pylint: disable=too-many-instance-attributes @@ -1139,7 +1154,9 @@ class BaseTest: # Get handles to logs before the files are deleted. logs = [] for log in self.logs: - logs.append((log, open(log, "r", errors='ignore'))) + # pylint: disable=consider-using-with + logs.append((log, + open(log, "r", errors='ignore', encoding='utf-8'))) self.classTeardown() for name, handle in logs: @@ -1159,8 +1176,8 @@ class GdbTest(BaseTest): def write_nop_program(self, count): for i in range(count): # 0x13 is nop - self.gdb.command("p *((int*) 0x%x)=0x13" % (self.hart.ram + i * 4)) - self.gdb.p("$pc=0x%x" % self.hart.ram) + self.gdb.command(f"p *((int*) 0x{self.hart.ram + i * 4:x})=0x13") + self.gdb.p(f"$pc=0x{self.hart.ram:x}") def classSetup(self): BaseTest.classSetup(self) @@ -1214,8 +1231,8 @@ class GdbTest(BaseTest): self.gdb.p("$pmpaddr0=0x" + "f" * (self.hart.xlen // 4)) else: # pmcfg0 readback matches write, so TOR is supported. - self.gdb.p("$pmpaddr0=0x%x" % - ((self.hart.ram + self.hart.ram_size) >> 2)) + self.gdb.p("$pmpaddr0=" + f"0x{(self.hart.ram + self.hart.ram_size) >> 2:x}") except CouldNotFetch: # PMP registers are optional pass @@ -1241,14 +1258,14 @@ class ExamineTarget(GdbTest): elif ((hart.misa & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) >> 126) == 3: misa_xlen = 128 else: - raise TestFailed("Couldn't determine XLEN from $misa (0x%x)" % - self.hart.misa) + raise TestFailed("Couldn't determine XLEN from $misa " + f"(0x{self.hart.misa:x})") if misa_xlen != hart.xlen: - raise TestFailed("MISA reported XLEN of %d but we were "\ - "expecting XLEN of %d\n" % (misa_xlen, hart.xlen)) + raise TestFailed(f"MISA reported XLEN of {misa_xlen} but we " + f"were expecting XLEN of {hart.xlen}\n") - txt += ("%d" % misa_xlen) + txt += f"{misa_xlen}" for i in range(26): if hart.misa & (1<<i): @@ -1260,7 +1277,7 @@ class TestFailed(Exception): Exception.__init__(self) self.message = message if comment: - self.message += ": %s" % comment + self.message += f": {comment}" class TestNotApplicable(Exception): def __init__(self, message=""): @@ -1269,32 +1286,32 @@ class TestNotApplicable(Exception): def assertEqual(a, b, comment=None): if a != b: - raise TestFailed("%r != %r" % (a, b), comment) + raise TestFailed(f"{a!r} != {b!r}", comment) def assertNotEqual(a, b, comment=None): if a == b: - raise TestFailed("%r == %r" % (a, b), comment) + raise TestFailed(f"{a!r} == {b!r}", comment) def assertIn(a, b): if a not in b: - raise TestFailed("%r not in %r" % (a, b)) + raise TestFailed(f"{a!r} not in {b!r}") def assertNotIn(a, b, comment=None): if a in b: - raise TestFailed("%r in %r" % (a, b), comment) + raise TestFailed(f"{a!r} in {b!r}", comment) def assertGreater(a, b): if not a > b: - raise TestFailed("%r not greater than %r" % (a, b)) + raise TestFailed(f"{a!r} not greater than {b!r}") def assertLess(a, b, comment=None): if not a < b: - raise TestFailed("%r not less than %r" % (a, b), comment) + raise TestFailed(f"{a!r} not less than {b!r}", comment) def assertTrue(a): if not a: - raise TestFailed("%r is not True" % a) + raise TestFailed(f"{a!r} is not True" % a) def assertRegex(text, regexp): if not re.search(regexp, text): - raise TestFailed("can't find %r in %r" % (regexp, text)) + raise TestFailed(f"can't find {regexp!r} in {text!r}") |