13 def ihex_line(address
, record_type
, data
):
14 assert len(data
) < 128
15 line
= ":%02X%04X%02X" % (len(data
), address
, record_type
)
17 check
+= address
% 256
23 line
+= "%02X" % value
24 line
+= "%02X\n" % ((256-check
)%256)
28 assert line
.startswith(":")
30 data_len
= int(line
[:2], 16)
31 address
= int(line
[2:6], 16)
32 record_type
= int(line
[6:8], 16)
34 for i
in range(data_len
):
35 data
+= "%c" % int(line
[8+2*i
:10+2*i
], 16)
36 return record_type
, address
, data
38 class DeleteServer(unittest
.TestCase
):
42 class SimpleRegisterTest(DeleteServer
):
44 self
.server
= target
.server()
45 self
.gdb
= testlib
.Gdb()
46 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
49 self
.gdb
.command("p *((int*) 0x%x)=0x13" % target
.ram
)
50 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 4))
51 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 8))
52 self
.gdb
.p("$pc=0x%x" % target
.ram
)
54 def check_reg(self
, name
):
55 a
= random
.randrange(1<<target
.xlen
)
56 b
= random
.randrange(1<<target
.xlen
)
57 self
.gdb
.p("$%s=0x%x" % (name
, a
))
59 self
.assertEqual(self
.gdb
.p("$%s" % name
), a
)
60 self
.gdb
.p("$%s=0x%x" % (name
, b
))
62 self
.assertEqual(self
.gdb
.p("$%s" % name
), b
)
65 # S0 is saved/restored in DSCRATCH
69 # S1 is saved/restored in Debug RAM
73 # T0 is not saved/restored at all
77 # T2 is not saved/restored at all
80 class SimpleMemoryTest(DeleteServer
):
82 self
.server
= target
.server()
83 self
.gdb
= testlib
.Gdb()
84 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
86 def access_test(self
, size
, data_type
):
87 a
= 0x86753095555aaaa & ((1<<(size
*8))-1)
88 b
= 0xdeadbeef12345678 & ((1<<(size
*8))-1)
89 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
, a
))
90 self
.gdb
.p("*((%s*)0x%x) = 0x%x" % (data_type
, target
.ram
+ size
, b
))
91 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
)), a
)
92 self
.assertEqual(self
.gdb
.p("*((%s*)0x%x)" % (data_type
, target
.ram
+ size
)), b
)
95 self
.access_test(1, 'char')
98 self
.access_test(2, 'short')
101 self
.access_test(4, 'long')
104 self
.access_test(8, 'long long')
106 def test_block(self
):
109 fd
= file("write.ihex", "w")
111 for i
in range(length
/ line_length
):
112 line_data
= "".join(["%c" % random
.randrange(256) for _
in range(line_length
)])
114 fd
.write(ihex_line(i
* line_length
, 0, line_data
))
117 self
.gdb
.command("restore write.ihex 0x%x" % target
.ram
)
118 for offset
in range(0, length
, 19*4) + [length
-4]:
119 value
= self
.gdb
.p("*((long*)0x%x)" % (target
.ram
+ offset
))
120 written
= ord(data
[offset
]) | \
121 (ord(data
[offset
+1]) << 8) | \
122 (ord(data
[offset
+2]) << 16) | \
123 (ord(data
[offset
+3]) << 24)
124 self
.assertEqual(value
, written
)
126 self
.gdb
.command("dump ihex memory read.ihex 0x%x 0x%x" % (target
.ram
,
127 target
.ram
+ length
))
128 for line
in file("read.ihex"):
129 record_type
, address
, line_data
= ihex_parse(line
)
130 if (record_type
== 0):
131 self
.assertEqual(line_data
, data
[address
:address
+len(line_data
)])
133 class InstantHaltTest(DeleteServer
):
135 self
.server
= target
.server()
136 self
.gdb
= testlib
.Gdb()
137 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
139 def test_instant_halt(self
):
140 self
.assertEqual(0x1000, self
.gdb
.p("$pc"))
141 # For some reason instret resets to 0.
142 self
.assertLess(self
.gdb
.p("$instret"), 8)
144 self
.assertNotEqual(0x1000, self
.gdb
.p("$pc"))
146 def test_change_pc(self
):
147 """Change the PC right as we come out of reset."""
149 self
.gdb
.command("p *((int*) 0x%x)=0x13" % target
.ram
)
150 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 4))
151 self
.gdb
.command("p *((int*) 0x%x)=0x13" % (target
.ram
+ 8))
152 self
.gdb
.p("$pc=0x%x" % target
.ram
)
154 self
.assertEqual((target
.ram
+ 4), self
.gdb
.p("$pc"))
156 self
.assertEqual((target
.ram
+ 8), self
.gdb
.p("$pc"))
158 class DebugTest(DeleteServer
):
160 # Include malloc so that gdb can make function calls. I suspect this
161 # malloc will silently blow through the memory set aside for it, so be
163 self
.binary
= target
.compile("programs/debug.c", "programs/checksum.c",
164 "programs/tiny-malloc.c", "-DDEFINE_MALLOC", "-DDEFINE_FREE")
165 self
.server
= target
.server()
166 self
.gdb
= testlib
.Gdb()
167 self
.gdb
.command("file %s" % self
.binary
)
168 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
173 output
= self
.gdb
.c()
174 self
.assertIn("Breakpoint", output
)
175 #TODO self.assertIn("_exit", output)
176 #TODO self.assertEqual(self.gdb.p("status"), 0xc86455d4)
177 # Use a0 until gdb can resolve "status"
178 self
.assertEqual(self
.gdb
.p("$a0") & 0xffffffff, 0xc86455d4)
180 def test_function_call(self
):
181 text
= "Howdy, Earth!"
182 gdb_length
= self
.gdb
.p('strlen("%s")' % text
)
183 self
.assertEqual(gdb_length
, len(text
))
186 def test_turbostep(self
):
187 """Single step a bunch of times."""
188 self
.gdb
.command("p i=0");
194 pc
= self
.gdb
.p("$pc")
195 self
.assertNotEqual(last_pc
, pc
)
196 if (last_pc
and pc
> last_pc
and pc
- last_pc
<= 4):
201 # Some basic sanity that we're not running between breakpoints or
203 self
.assertGreater(jumps
, 10)
204 self
.assertGreater(advances
, 50)
209 def test_symbols(self
):
212 output
= self
.gdb
.c()
213 self
.assertIn(", main ", output
)
214 output
= self
.gdb
.c()
215 self
.assertIn(", rot13 ", output
)
217 def test_breakpoint(self
):
219 # The breakpoint should be hit exactly 2 times.
221 output
= self
.gdb
.c()
223 self
.assertIn("Breakpoint ", output
)
224 #TODO self.assertIn("rot13 ", output)
227 def test_hwbp_1(self
):
228 self
.gdb
.hbreak("rot13")
229 # The breakpoint should be hit exactly 2 times.
231 output
= self
.gdb
.c()
233 self
.assertIn("Breakpoint ", output
)
234 #TODO self.assertIn("rot13 ", output)
237 def test_hwbp_2(self
):
238 self
.gdb
.hbreak("main")
239 self
.gdb
.hbreak("rot13")
240 # We should hit 3 breakpoints.
242 output
= self
.gdb
.c()
244 self
.assertIn("Breakpoint ", output
)
245 #TODO self.assertIn("rot13 ", output)
248 def test_too_many_hwbp(self
):
250 self
.gdb
.hbreak("*rot13 + %d" % (i
* 4))
252 output
= self
.gdb
.c()
253 self
.assertIn("Cannot insert hardware breakpoint", output
)
254 # Clean up, otherwise the hardware breakpoints stay set and future
256 self
.gdb
.command("D")
258 def test_registers(self
):
259 # Get to a point in the code where some registers have actually been
264 # Try both forms to test gdb.
265 for cmd
in ("info all-registers", "info registers all"):
266 output
= self
.gdb
.command(cmd
)
267 self
.assertNotIn("Could not", output
)
268 for reg
in ('zero', 'ra', 'sp', 'gp', 'tp'):
269 self
.assertIn(reg
, output
)
272 # mcpuid is one of the few registers that should have the high bit set
274 # Leave this commented out until gdb and spike agree on the encoding of
275 # mcpuid (which is going to be renamed to misa in any case).
276 #self.assertRegexpMatches(output, ".*mcpuid *0x80")
279 # The instret register should always be changing.
282 # instret = self.gdb.p("$instret")
283 # self.assertNotEqual(instret, last_instret)
284 # last_instret = instret
289 def test_interrupt(self
):
290 """Sending gdb ^C while the program is running should cause it to halt."""
291 self
.gdb
.b("main:start")
294 self
.gdb
.c(wait
=False)
296 output
= self
.gdb
.interrupt()
297 #TODO: assert "main" in output
298 self
.assertGreater(self
.gdb
.p("j"), 10)
302 class StepTest(DeleteServer
):
304 self
.binary
= target
.compile("programs/step.S")
305 self
.server
= target
.server()
306 self
.gdb
= testlib
.Gdb()
307 self
.gdb
.command("file %s" % self
.binary
)
308 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
314 main
= self
.gdb
.p("$pc")
315 for expected
in (4, 8, 0xc, 0x10, 0x18, 0x1c, 0x28, 0x20, 0x2c, 0x2c):
317 pc
= self
.gdb
.p("$pc")
318 self
.assertEqual("%x" % pc
, "%x" % (expected
+ main
))
320 class RegsTest(DeleteServer
):
322 self
.binary
= target
.compile("programs/regs.S")
323 self
.server
= target
.server()
324 self
.gdb
= testlib
.Gdb()
325 self
.gdb
.command("file %s" % self
.binary
)
326 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
329 self
.gdb
.b("handle_trap")
332 def test_write_gprs(self
):
333 regs
= [("x%d" % n
) for n
in range(2, 32)]
335 self
.gdb
.p("$pc=write_regs")
336 for i
, r
in enumerate(regs
):
337 self
.gdb
.command("p $%s=%d" % (r
, (0xdeadbeef<<i
)+17))
338 self
.gdb
.command("p $x1=data")
339 self
.gdb
.command("b all_done")
340 output
= self
.gdb
.c()
341 self
.assertIn("Breakpoint ", output
)
343 # Just to get this data in the log.
344 self
.gdb
.command("x/30gx data")
345 self
.gdb
.command("info registers")
346 for n
in range(len(regs
)):
347 self
.assertEqual(self
.gdb
.x("data+%d" % (8*n
), 'g'),
348 ((0xdeadbeef<<n
)+17) & ((1<<target
.xlen
)-1))
350 def test_write_csrs(self
):
351 # As much a test of gdb as of the simulator.
352 self
.gdb
.p("$mscratch=0")
354 self
.assertEqual(self
.gdb
.p("$mscratch"), 0)
355 self
.gdb
.p("$mscratch=123")
357 self
.assertEqual(self
.gdb
.p("$mscratch"), 123)
359 self
.gdb
.command("p $pc=write_regs")
360 self
.gdb
.command("p $a0=data")
361 self
.gdb
.command("b all_done")
362 self
.gdb
.command("c")
364 self
.assertEqual(123, self
.gdb
.p("$mscratch"))
365 self
.assertEqual(123, self
.gdb
.p("$x1"))
366 self
.assertEqual(123, self
.gdb
.p("$csr832"))
368 class DownloadTest(DeleteServer
):
370 length
= min(2**20, target
.ram_size
- 2048)
371 fd
= file("download.c", "w")
372 fd
.write("#include <stdint.h>\n")
373 fd
.write("unsigned int crc32a(uint8_t *message, unsigned int size);\n")
374 fd
.write("uint32_t length = %d;\n" % length
)
375 fd
.write("uint8_t d[%d] = {\n" % length
)
377 for i
in range(length
/ 16):
378 fd
.write(" /* 0x%04x */ " % (i
* 16));
380 value
= random
.randrange(1<<8)
381 fd
.write("%d, " % value
)
382 self
.crc
= binascii
.crc32("%c" % value
, self
.crc
)
385 fd
.write("uint8_t *data = &d[0];\n");
386 fd
.write("uint32_t main() { return crc32a(data, length); }\n")
392 self
.binary
= target
.compile("download.c", "programs/checksum.c")
393 self
.server
= target
.server()
394 self
.gdb
= testlib
.Gdb()
395 self
.gdb
.command("file %s" % self
.binary
)
396 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
398 def test_download(self
):
399 output
= self
.gdb
.load()
400 self
.gdb
.command("b _exit")
402 self
.assertEqual(self
.gdb
.p("status"), self
.crc
)
404 class MprvTest(DeleteServer
):
406 self
.binary
= target
.compile("programs/mprv.S")
407 self
.server
= target
.server()
408 self
.gdb
= testlib
.Gdb()
409 self
.gdb
.command("file %s" % self
.binary
)
410 self
.gdb
.command("target extended-remote localhost:%d" % self
.server
.port
)
414 """Test that the debugger can access memory when MPRV is set."""
415 self
.gdb
.c(wait
=False)
418 output
= self
.gdb
.command("p/x *(int*)(((char*)&data)-0x80000000)")
419 self
.assertIn("0xbead", output
)
421 class Target(object):
425 raise NotImplementedError
427 def compile(self
, *sources
):
428 return testlib
.compile(sources
+
429 ("programs/entry.S", "programs/init.c",
431 "-T", "targets/%s/link.lds" % (self
.directory
or self
.name
),
433 "-mcmodel=medany"), xlen
=self
.xlen
)
435 class Spike64Target(Target
):
439 ram_size
= 5 * 1024 * 1024
442 return testlib
.Spike(parsed
.cmd
, halted
=True)
444 class Spike32Target(Target
):
449 ram_size
= 5 * 1024 * 1024
452 return testlib
.Spike(parsed
.cmd
, halted
=True, xlen
=32)
454 class MicroSemiTarget(Target
):
461 return testlib
.Openocd(cmd
=parsed
.cmd
,
462 config
="targets/%s/openocd.cfg" % self
.name
)
471 parser
= argparse
.ArgumentParser(
473 Example command line from the real world:
474 Run all RegsTest cases against a MicroSemi m2gl_m2s board, with custom openocd command:
475 ./gdbserver.py --m2gl_m2s --cmd "$HOME/SiFive/openocd/src/openocd -s $HOME/SiFive/openocd/tcl -d" -- -vf RegsTest
477 group
= parser
.add_mutually_exclusive_group(required
=True)
479 group
.add_argument("--%s" % t
.name
, action
="store_const", const
=t
,
481 parser
.add_argument("--cmd",
482 help="The command to use to start the debug server.")
483 parser
.add_argument("unittest", nargs
="*")
485 parsed
= parser
.parse_args()
488 target
= parsed
.target()
489 unittest
.main(argv
=[sys
.argv
[0]] + parsed
.unittest
)
491 # TROUBLESHOOTING TIPS
492 # If a particular test fails, run just that one test, eg.:
493 # ./tests/gdbserver.py MprvTest.test_mprv
494 # Then inspect gdb.log and spike.log to see what happened in more detail.
496 if __name__
== '__main__':