X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=debug%2Fgdbserver.py;h=0ae75a7f9cca13b5de36302565013fb9310af712;hb=edfe598fd06366170628e88aa6dee762d2c37bbc;hp=a527ad0baad63488ebabfbd1207749cd44897a27;hpb=868b2112753be7c1dd8d071e47614fd0dec6649f;p=riscv-tests.git diff --git a/debug/gdbserver.py b/debug/gdbserver.py index a527ad0..0ae75a7 100755 --- a/debug/gdbserver.py +++ b/debug/gdbserver.py @@ -10,6 +10,26 @@ import time import random import binascii +MSTATUS_UIE = 0x00000001 +MSTATUS_SIE = 0x00000002 +MSTATUS_HIE = 0x00000004 +MSTATUS_MIE = 0x00000008 +MSTATUS_UPIE = 0x00000010 +MSTATUS_SPIE = 0x00000020 +MSTATUS_HPIE = 0x00000040 +MSTATUS_MPIE = 0x00000080 +MSTATUS_SPP = 0x00000100 +MSTATUS_HPP = 0x00000600 +MSTATUS_MPP = 0x00001800 +MSTATUS_FS = 0x00006000 +MSTATUS_XS = 0x00018000 +MSTATUS_MPRV = 0x00020000 +MSTATUS_PUM = 0x00040000 +MSTATUS_MXR = 0x00080000 +MSTATUS_VM = 0x1F000000 +MSTATUS32_SD = 0x80000000 +MSTATUS64_SD = 0x8000000000000000 + def ihex_line(address, record_type, data): assert len(data) < 128 line = ":%02X%04X%02X" % (len(data), address, record_type) @@ -43,6 +63,10 @@ class SimpleRegisterTest(DeleteServer): def setUp(self): self.server = target.server() self.gdb = testlib.Gdb() + # For now gdb has to be told what the architecture is when it's not + # given an ELF file. + self.gdb.command("set arch riscv:rv%d" % target.xlen) + self.gdb.command("target extended-remote localhost:%d" % self.server.port) # 0x13 is nop @@ -81,9 +105,12 @@ class SimpleMemoryTest(DeleteServer): def setUp(self): self.server = target.server() self.gdb = testlib.Gdb() + self.gdb.command("set arch riscv:rv%d" % target.xlen) self.gdb.command("target extended-remote localhost:%d" % self.server.port) def access_test(self, size, data_type): + self.assertEqual(self.gdb.p("sizeof(%s)" % data_type), + size) a = 0x86753095555aaaa & ((1<<(size*8))-1) b = 0xdeadbeef12345678 & ((1<<(size*8))-1) self.gdb.p("*((%s*)0x%x) = 0x%x" % (data_type, target.ram, a)) @@ -98,7 +125,7 @@ class SimpleMemoryTest(DeleteServer): self.access_test(2, 'short') def test_32(self): - self.access_test(4, 'long') + self.access_test(4, 'int') def test_64(self): self.access_test(8, 'long long') @@ -106,26 +133,27 @@ class SimpleMemoryTest(DeleteServer): def test_block(self): length = 1024 line_length = 16 - fd = file("write.ihex", "w") + a = tempfile.NamedTemporaryFile(suffix=".ihex") data = "" for i in range(length / line_length): line_data = "".join(["%c" % random.randrange(256) for _ in range(line_length)]) data += line_data - fd.write(ihex_line(i * line_length, 0, line_data)) - fd.close() + a.write(ihex_line(i * line_length, 0, line_data)) + a.flush() - self.gdb.command("restore write.ihex 0x%x" % target.ram) + self.gdb.command("restore %s 0x%x" % (a.name, target.ram)) for offset in range(0, length, 19*4) + [length-4]: - value = self.gdb.p("*((long*)0x%x)" % (target.ram + offset)) + value = self.gdb.p("*((int*)0x%x)" % (target.ram + offset)) written = ord(data[offset]) | \ (ord(data[offset+1]) << 8) | \ (ord(data[offset+2]) << 16) | \ (ord(data[offset+3]) << 24) self.assertEqual(value, written) - self.gdb.command("dump ihex memory read.ihex 0x%x 0x%x" % (target.ram, + b = tempfile.NamedTemporaryFile(suffix=".ihex") + self.gdb.command("dump ihex memory %s 0x%x 0x%x" % (b.name, target.ram, target.ram + length)) - for line in file("read.ihex"): + for line in b: record_type, address, line_data = ihex_parse(line) if (record_type == 0): self.assertEqual(line_data, data[address:address+len(line_data)]) @@ -134,14 +162,15 @@ class InstantHaltTest(DeleteServer): def setUp(self): self.server = target.server() self.gdb = testlib.Gdb() + self.gdb.command("set arch riscv:rv%d" % target.xlen) self.gdb.command("target extended-remote localhost:%d" % self.server.port) def test_instant_halt(self): - self.assertEqual(0x1000, self.gdb.p("$pc")) - # For some reason instret resets to 0. - self.assertLess(self.gdb.p("$instret"), 8) - self.gdb.stepi() - self.assertNotEqual(0x1000, self.gdb.p("$pc")) + self.assertEqual(target.reset_vector, self.gdb.p("$pc")) + # mcycle and minstret have no defined reset value. + mstatus = self.gdb.p("$mstatus") + self.assertEqual(mstatus & (MSTATUS_MIE | MSTATUS_MPRV | + MSTATUS_VM), 0) def test_change_pc(self): """Change the PC right as we come out of reset.""" @@ -157,7 +186,11 @@ class InstantHaltTest(DeleteServer): class DebugTest(DeleteServer): def setUp(self): - self.binary = target.compile("programs/debug.c", "programs/checksum.c") + # Include malloc so that gdb can make function calls. I suspect this + # malloc will silently blow through the memory set aside for it, so be + # careful. + self.binary = target.compile("programs/debug.c", "programs/checksum.c", + "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE") self.server = target.server() self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) @@ -165,27 +198,58 @@ class DebugTest(DeleteServer): self.gdb.load() self.gdb.b("_exit") - def exit(self): + def exit(self, expected_result = 0xc86455d4): output = self.gdb.c() self.assertIn("Breakpoint", output) - #TODO self.assertIn("_exit", output) - #TODO self.assertEqual(self.gdb.p("status"), 0xc86455d4) - # Use a0 until gdb can resolve "status" - self.assertEqual(self.gdb.p("$a0") & 0xffffffff, 0xc86455d4) + self.assertIn("_exit", output) + self.assertEqual(self.gdb.p("status"), expected_result) + + def test_function_call(self): + self.gdb.b("main:start") + self.gdb.c() + text = "Howdy, Earth!" + gdb_length = self.gdb.p('strlen("%s")' % text) + self.assertEqual(gdb_length, len(text)) + self.exit() + + def test_change_string(self): + text = "This little piggy went to the market." + self.gdb.b("main:start") + self.gdb.c() + self.gdb.p('fox = "%s"' % text) + self.exit(0x43b497b8) def test_turbostep(self): """Single step a bunch of times.""" self.gdb.command("p i=0"); last_pc = None + advances = 0 + jumps = 0 for _ in range(100): self.gdb.stepi() - pc = self.gdb.command("p $pc") + pc = self.gdb.p("$pc") self.assertNotEqual(last_pc, pc) + if (last_pc and pc > last_pc and pc - last_pc <= 4): + advances += 1 + else: + jumps += 1 last_pc = pc + # Some basic sanity that we're not running between breakpoints or + # something. + self.assertGreater(jumps, 10) + self.assertGreater(advances, 50) def test_exit(self): self.exit() + def test_symbols(self): + self.gdb.b("main") + self.gdb.b("rot13") + output = self.gdb.c() + self.assertIn(", main ", output) + output = self.gdb.c() + self.assertIn(", rot13 ", output) + def test_breakpoint(self): self.gdb.b("rot13") # The breakpoint should be hit exactly 2 times. @@ -197,6 +261,9 @@ class DebugTest(DeleteServer): self.exit() def test_hwbp_1(self): + if target.instruction_hardware_breakpoint_count < 1: + return + self.gdb.hbreak("rot13") # The breakpoint should be hit exactly 2 times. for i in range(2): @@ -207,6 +274,9 @@ class DebugTest(DeleteServer): self.exit() def test_hwbp_2(self): + if target.instruction_hardware_breakpoint_count < 2: + return + self.gdb.hbreak("main") self.gdb.hbreak("rot13") # We should hit 3 breakpoints. @@ -223,6 +293,9 @@ class DebugTest(DeleteServer): output = self.gdb.c() self.assertIn("Cannot insert hardware breakpoint", output) + # Clean up, otherwise the hardware breakpoints stay set and future + # tests may fail. + self.gdb.command("D") def test_registers(self): # Get to a point in the code where some registers have actually been @@ -268,6 +341,24 @@ class DebugTest(DeleteServer): self.gdb.p("i=0"); self.exit() +class StepTest(DeleteServer): + def setUp(self): + self.binary = target.compile("programs/step.S") + self.server = target.server() + self.gdb = testlib.Gdb() + self.gdb.command("file %s" % self.binary) + self.gdb.command("target extended-remote localhost:%d" % self.server.port) + self.gdb.load() + self.gdb.b("main") + self.gdb.c() + + def test_step(self): + main = self.gdb.p("$pc") + for expected in (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c): + self.gdb.stepi() + pc = self.gdb.p("$pc") + self.assertEqual("%x" % pc, "%x" % (expected + main)) + class RegsTest(DeleteServer): def setUp(self): self.binary = target.compile("programs/regs.S") @@ -318,29 +409,29 @@ class RegsTest(DeleteServer): class DownloadTest(DeleteServer): def setUp(self): - length = 2**20 - fd = file("download.c", "w") - fd.write("#include \n") - fd.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n") - fd.write("uint32_t length = %d;\n" % length) - fd.write("uint8_t d[%d] = {\n" % length) + length = min(2**20, target.ram_size - 2048) + download_c = tempfile.NamedTemporaryFile(prefix="download_", suffix=".c") + download_c.write("#include \n") + download_c.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n") + download_c.write("uint32_t length = %d;\n" % length) + download_c.write("uint8_t d[%d] = {\n" % length) self.crc = 0 for i in range(length / 16): - fd.write(" /* 0x%04x */ " % (i * 16)); + download_c.write(" /* 0x%04x */ " % (i * 16)); for _ in range(16): value = random.randrange(1<<8) - fd.write("%d, " % value) + download_c.write("%d, " % value) self.crc = binascii.crc32("%c" % value, self.crc) - fd.write("\n"); - fd.write("};\n"); - fd.write("uint8_t *data = &d[0];\n"); - fd.write("uint32_t main() { return crc32a(data, length); }\n") - fd.close() + download_c.write("\n"); + download_c.write("};\n"); + download_c.write("uint8_t *data = &d[0];\n"); + download_c.write("uint32_t main() { return crc32a(data, length); }\n") + download_c.flush() if self.crc < 0: self.crc += 2**32 - self.binary = target.compile("download.c", "programs/checksum.c") + self.binary = target.compile(download_c.name, "programs/checksum.c") self.server = target.server() self.gdb = testlib.Gdb() self.gdb.command("file %s" % self.binary) @@ -364,6 +455,7 @@ class MprvTest(DeleteServer): def test_mprv(self): """Test that the debugger can access memory when MPRV is set.""" self.gdb.c(wait=False) + time.sleep(0.5) self.gdb.interrupt() output = self.gdb.command("p/x *(int*)(((char*)&data)-0x80000000)") self.assertIn("0xbead", output) @@ -375,17 +467,30 @@ class Target(object): raise NotImplementedError def compile(self, *sources): - return testlib.compile(sources + + binary_name = "%s_%s" % ( + self.name, + os.path.basename(os.path.splitext(sources[0])[0])) + if parsed.isolate: + self.temporary_binary = tempfile.NamedTemporaryFile( + prefix=binary_name + "_") + binary_name = self.temporary_binary.name + testlib.compile(sources + ("programs/entry.S", "programs/init.c", "-I", "../env", "-T", "targets/%s/link.lds" % (self.directory or self.name), "-nostartfiles", - "-mcmodel=medany"), xlen=self.xlen) + "-mcmodel=medany", + "-o", binary_name), + xlen=self.xlen) + return binary_name class Spike64Target(Target): name = "spike" xlen = 64 ram = 0x80010000 + ram_size = 5 * 1024 * 1024 + instruction_hardware_breakpoint_count = 0 + reset_vector = 0x1000 def server(self): return testlib.Spike(parsed.cmd, halted=True) @@ -395,6 +500,9 @@ class Spike32Target(Target): directory = "spike" xlen = 32 ram = 0x80010000 + ram_size = 5 * 1024 * 1024 + instruction_hardware_breakpoint_count = 0 + reset_vector = 0x1000 def server(self): return testlib.Spike(parsed.cmd, halted=True, xlen=32) @@ -403,6 +511,8 @@ class MicroSemiTarget(Target): name = "m2gl_m2s" xlen = 32 ram = 0x80000000 + ram_size = 16 * 1024 + instruction_hardware_breakpoint_count = 2 def server(self): return testlib.Openocd(cmd=parsed.cmd, @@ -427,6 +537,10 @@ def main(): dest="target") parser.add_argument("--cmd", help="The command to use to start the debug server.") + parser.add_argument("--isolate", action="store_true", + help="Try to run in such a way that multiple instances can run at " + "the same time. This may make it harder to debug a failure if it " + "does occur.") parser.add_argument("unittest", nargs="*") global parsed parsed = parser.parse_args()