From 806deb8e53e029df31defc88d09c6c2c2b08e8f3 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 22 Jun 2017 18:37:55 -0700 Subject: [PATCH] Add basic multicore test. --- debug/gdbserver.py | 64 ++++++++++++++++++++++++++++++- debug/programs/infinite_loop.S | 69 ++++++++++++++++++---------------- debug/testlib.py | 37 ++++++++++++++---- 3 files changed, 128 insertions(+), 42 deletions(-) diff --git a/debug/gdbserver.py b/debug/gdbserver.py index 4dc00c4..2dcd404 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -10,7 +10,7 @@ import os import targets import testlib -from testlib import assertEqual, assertNotEqual, assertIn +from testlib import assertEqual, assertNotEqual, assertIn, assertNotIn from testlib import assertGreater, assertRegexpMatches, assertLess from testlib import GdbTest @@ -399,6 +399,68 @@ class UserInterrupt(DebugTest): self.gdb.p("i=0") self.exit() +class MulticoreTest(GdbTest): + compile_args = ("programs/infinite_loop.S", ) + + def setup(self): + self.gdb.load() + self.gdb.b("main") + self.gdb.b("main_end") + self.gdb.command("set non-stop on") + self.gdb.c() + + def test(self): + threads = self.gdb.threads() + if len(threads) < 2: + return 'not_applicable' + # Run through the entire loop. + for t in threads: + self.gdb.thread(t) + self.gdb.p("$pc=_start") + # Run to main + for t in threads: + self.gdb.thread(t) + self.gdb.c() + for t in self.gdb.threads(): + assertIn("main", t.frame) + # Run to end + for t in threads: + self.gdb.thread(t) + self.gdb.c() + hart_ids = [] + for t in self.gdb.threads(): + assertIn("main_end", t.frame) + # Check register values. + self.gdb.thread(t) + hart_id = self.gdb.p("$x1") + assertNotIn(hart_id, hart_ids) + hart_ids.append(hart_id) + for n in range(2, 32): + value = self.gdb.p("$x%d" % n) + assertEqual(value, hart_ids[-1] + n - 1) + + # Confirmed that we read different register values for different harts. + # Write a new value to x1, and run through the add sequence again. + + # This part isn't working right, because gdb doesn't resume Thread 2 + # when asked. I don't know the root cause for that, but up to this + # point the test is still useful. + +# for t in threads: +# self.gdb.thread(t) +# self.gdb.p("$x1=0x%x" % (int(t.id) + 0x800)) +# self.gdb.p("$pc=main_post_csrr") +# for t in threads: +# self.gdb.thread(t) +# self.gdb.c() +# for t in self.gdb.threads(): +# assertIn("main_end", t.frame) +# # Check register values. +# self.gdb.thread(t) +# for n in range(1, 32): +# value = self.gdb.p("$x%d" % n) +# assertEqual(value, int(t.id) + 0x800 + n - 1) + class StepTest(GdbTest): compile_args = ("programs/step.S", ) diff --git a/debug/programs/infinite_loop.S b/debug/programs/infinite_loop.S index 4b83143..6f15f45 100644 --- a/debug/programs/infinite_loop.S +++ b/debug/programs/infinite_loop.S @@ -1,40 +1,43 @@ #include "encoding.h" .global main + .global main_end + .global main_post_csrr // Load constants into all registers so we can test no register are // clobbered after attaching. main: - csrr x1, CSR_MISA - slli x2, x1, 1 - slli x3, x2, 1 - slli x4, x3, 1 - slli x5, x4, 1 - slli x6, x5, 1 - slli x7, x6, 1 - slli x8, x7, 1 - slli x9, x8, 1 - slli x10, x9, 1 - slli x11, x10, 1 - slli x12, x11, 1 - slli x13, x12, 1 - slli x14, x13, 1 - slli x15, x14, 1 - slli x16, x15, 1 - slli x17, x16, 1 - slli x18, x17, 1 - slli x19, x18, 1 - slli x20, x19, 1 - slli x21, x20, 1 - slli x22, x21, 1 - slli x23, x22, 1 - slli x24, x23, 1 - slli x25, x24, 1 - slli x26, x25, 1 - slli x27, x26, 1 - slli x28, x27, 1 - slli x29, x28, 1 - slli x30, x29, 1 - slli x31, x30, 1 -1: - j 1b + csrr x1, CSR_MHARTID +main_post_csrr: + addi x2, x1, 1 + addi x3, x2, 1 + addi x4, x3, 1 + addi x5, x4, 1 + addi x6, x5, 1 + addi x7, x6, 1 + addi x8, x7, 1 + addi x9, x8, 1 + addi x10, x9, 1 + addi x11, x10, 1 + addi x12, x11, 1 + addi x13, x12, 1 + addi x14, x13, 1 + addi x15, x14, 1 + addi x16, x15, 1 + addi x17, x16, 1 + addi x18, x17, 1 + addi x19, x18, 1 + addi x20, x19, 1 + addi x21, x20, 1 + addi x22, x21, 1 + addi x23, x22, 1 + addi x24, x23, 1 + addi x25, x24, 1 + addi x26, x25, 1 + addi x27, x26, 1 + addi x28, x27, 1 + addi x29, x28, 1 + addi x30, x29, 1 + addi x31, x30, 1 +main_end: + j main diff --git a/debug/testlib.py b/debug/testlib.py index b8ff5c2..b59e6e8 100644 --- a/debug/testlib.py +++ b/debug/testlib.py @@ -1,3 +1,4 @@ +import collections import os.path import random import re @@ -294,6 +295,9 @@ class CannotAccess(Exception): Exception.__init__(self) self.address = address +Thread = collections.namedtuple('Thread', ('id', 'target_id', 'name', + 'frame')) + class Gdb(object): logfile = tempfile.NamedTemporaryFile(prefix="gdb", suffix=".log") logname = logfile.name @@ -320,13 +324,17 @@ class Gdb(object): self.child.expect(r"\(gdb\)", timeout=timeout) return self.child.before.strip() - def c(self, wait=True, timeout=-1): + def c(self, wait=True, timeout=-1, async=False): + if async: + async = "&" + else: + async = "" if wait: - output = self.command("c", timeout=timeout) + output = self.command("c%s" % async, timeout=timeout) assert "Continuing" in output return output else: - self.child.sendline("c") + self.child.sendline("c%s" % async) self.child.expect("Continuing") def interrupt(self): @@ -380,6 +388,22 @@ class Gdb(object): assert "Hardware assisted breakpoint" in output return output + def threads(self): + output = self.command("info threads") + threads = [] + for line in output.splitlines(): + m = re.match( + r"[\s\*]*(\d+)\s*Thread (\d+)\s*\(Name: ([^\)]+)\s*(.*)", + line) + if m: + threads.append(Thread(*m.groups())) + if not threads: + threads.append(Thread('1', '1', 'Default', '???')) + return threads + + def thread(self, thread): + return self.command("thread %s" % thread.id) + def run_all_tests(module, target, parsed): if not os.path.exists(parsed.logs): os.makedirs(parsed.logs) @@ -594,11 +618,8 @@ class GdbTest(BaseTest): "target extended-remote localhost:%d" % self.server.port) # Select a random thread. # TODO: Allow a command line option to force a specific thread. - output = self.gdb.command("info threads") - threads = re.findall(r"Thread (\d+)", output) - if threads: - thread = random.choice(threads) - self.gdb.command("thread %s" % thread) + thread = random.choice(self.gdb.threads()) + self.gdb.thread(thread) # FIXME: OpenOCD doesn't handle PRIV now #self.gdb.p("$priv=3") -- 2.30.2