tests/guest-debug: introduce basic gdbstub tests

The aim of these tests is to combine with an appropriate kernel
image (with symbol-file vmlinux) and check it behaves as it should.
Given a kernel it checks:

  - single step
  - software breakpoint
  - hardware breakpoint
  - access, read and write watchpoints

On success it returns 0 to the calling process.

I've not plumbed this into the "make check" logic though as we need a
solution for providing non-host binaries to the tests. However the test
is structured to work with pretty much any Linux kernel image as it
uses the basic kernel_init code which is common across architectures.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Message-id: 1449599553-24713-7-git-send-email-alex.bennee@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Alex Bennée 2015-12-17 13:37:15 +00:00 committed by Peter Maydell
parent 34c45d5302
commit 261f4d6d3e

View File

@ -0,0 +1,176 @@
#
# This script needs to be run on startup
# qemu -kernel ${KERNEL} -s -S
# and then:
# gdb ${KERNEL}.vmlinux -x ${QEMU_SRC}/tests/guest-debug/test-gdbstub.py
import gdb
failcount = 0
def report(cond, msg):
"Report success/fail of test"
if cond:
print ("PASS: %s" % (msg))
else:
print ("FAIL: %s" % (msg))
failcount += 1
def check_step():
"Step an instruction, check it moved."
start_pc = gdb.parse_and_eval('$pc')
gdb.execute("si")
end_pc = gdb.parse_and_eval('$pc')
return not (start_pc == end_pc)
def check_break(sym_name):
"Setup breakpoint, continue and check we stopped."
sym, ok = gdb.lookup_symbol(sym_name)
bp = gdb.Breakpoint(sym_name)
gdb.execute("c")
# hopefully we came back
end_pc = gdb.parse_and_eval('$pc')
print ("%s == %s %d" % (end_pc, sym.value(), bp.hit_count))
bp.delete()
# can we test we hit bp?
return end_pc == sym.value()
# We need to do hbreak manually as the python interface doesn't export it
def check_hbreak(sym_name):
"Setup hardware breakpoint, continue and check we stopped."
sym, ok = gdb.lookup_symbol(sym_name)
gdb.execute("hbreak %s" % (sym_name))
gdb.execute("c")
# hopefully we came back
end_pc = gdb.parse_and_eval('$pc')
print ("%s == %s" % (end_pc, sym.value()))
if end_pc == sym.value():
gdb.execute("d 1")
return True
else:
return False
class WatchPoint(gdb.Breakpoint):
def get_wpstr(self, sym_name):
"Setup sym and wp_str for given symbol."
self.sym, ok = gdb.lookup_symbol(sym_name)
wp_addr = gdb.parse_and_eval(sym_name).address
self.wp_str = '*(%(type)s)(&%(address)s)' % dict(
type = wp_addr.type, address = sym_name)
return(self.wp_str)
def __init__(self, sym_name, type):
wp_str = self.get_wpstr(sym_name)
super(WatchPoint, self).__init__(wp_str, gdb.BP_WATCHPOINT, type)
def stop(self):
end_pc = gdb.parse_and_eval('$pc')
print ("HIT WP @ %s" % (end_pc))
return True
def do_one_watch(sym, wtype, text):
wp = WatchPoint(sym, wtype)
gdb.execute("c")
report_str = "%s for %s (%s)" % (text, sym, wp.sym.value())
if wp.hit_count > 0:
report(True, report_str)
wp.delete()
else:
report(False, report_str)
def check_watches(sym_name):
"Watch a symbol for any access."
# Should hit for any read
do_one_watch(sym_name, gdb.WP_ACCESS, "awatch")
# Again should hit for reads
do_one_watch(sym_name, gdb.WP_READ, "rwatch")
# Finally when it is written
do_one_watch(sym_name, gdb.WP_WRITE, "watch")
class CatchBreakpoint(gdb.Breakpoint):
def __init__(self, sym_name):
super(CatchBreakpoint, self).__init__(sym_name)
self.sym, ok = gdb.lookup_symbol(sym_name)
def stop(self):
end_pc = gdb.parse_and_eval('$pc')
print ("CB: %s == %s" % (end_pc, self.sym.value()))
if end_pc == self.sym.value():
report(False, "Hit final catchpoint")
def run_test():
"Run throught the tests one by one"
print ("Checking we can step the first few instructions")
step_ok = 0
for i in range(3):
if check_step():
step_ok += 1
report(step_ok == 3, "single step in boot code")
print ("Checking HW breakpoint works")
break_ok = check_hbreak("kernel_init")
report(break_ok, "hbreak @ kernel_init")
# Can't set this up until we are in the kernel proper
# if we make it to run_init_process we've over-run and
# one of the tests failed
print ("Setup catch-all for run_init_process")
cbp = CatchBreakpoint("run_init_process")
cpb2 = CatchBreakpoint("try_to_run_init_process")
print ("Checking Normal breakpoint works")
break_ok = check_break("wait_for_completion")
report(break_ok, "break @ wait_for_completion")
print ("Checking watchpoint works")
check_watches("system_state")
#
# This runs as the script it sourced (via -x)
#
try:
print ("Connecting to remote")
gdb.execute("target remote localhost:1234")
# These are not very useful in scripts
gdb.execute("set pagination off")
gdb.execute("set confirm off")
# Run the actual tests
run_test()
except:
print ("GDB Exception: %s" % (sys.exc_info()[0]))
failcount += 1
import code
code.InteractiveConsole(locals=globals()).interact()
raise
# Finally kill the inferior and exit gdb with a count of failures
gdb.execute("kill")
exit(failcount)