From patchwork Tue Dec 8 18:32:33 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Alex_Benn=C3=A9e?= X-Patchwork-Id: 57889 Delivered-To: patch@linaro.org Received: by 10.112.147.194 with SMTP id tm2csp201109lbb; Tue, 8 Dec 2015 10:34:05 -0800 (PST) X-Received: by 10.55.81.3 with SMTP id f3mr1447834qkb.35.1449599645585; Tue, 08 Dec 2015 10:34:05 -0800 (PST) Return-Path: Received: from lists.gnu.org (lists.gnu.org. [2001:4830:134:3::11]) by mx.google.com with ESMTPS id w202si4564505qka.86.2015.12.08.10.34.05 for (version=TLS1 cipher=AES128-SHA bits=128/128); Tue, 08 Dec 2015 10:34:05 -0800 (PST) Received-SPF: pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) client-ip=2001:4830:134:3::11; Authentication-Results: mx.google.com; spf=pass (google.com: domain of qemu-devel-bounces+patch=linaro.org@nongnu.org designates 2001:4830:134:3::11 as permitted sender) smtp.mailfrom=qemu-devel-bounces+patch=linaro.org@nongnu.org; dkim=fail header.i=@linaro-org.20150623.gappssmtp.com Received: from localhost ([::1]:32969 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a6N5R-0004zc-2w for patch@linaro.org; Tue, 08 Dec 2015 13:34:05 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50822) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a6N4T-0003vx-9Q for qemu-devel@nongnu.org; Tue, 08 Dec 2015 13:33:06 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1a6N4Q-0000jy-6x for qemu-devel@nongnu.org; Tue, 08 Dec 2015 13:33:05 -0500 Received: from mail-wm0-x229.google.com ([2a00:1450:400c:c09::229]:35415) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1a6N4P-0000je-Ue for qemu-devel@nongnu.org; Tue, 08 Dec 2015 13:33:02 -0500 Received: by wmuu63 with SMTP id u63so192174647wmu.0 for ; Tue, 08 Dec 2015 10:33:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro-org.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; bh=VquFXPHNpLfUvHIT7Ay8v1oK+AnegArNrc6epURKSws=; b=hxMtTNbxwkVeLU6GzWY1cCMK+VK/8ycxsMotXPZDtGNq66sA/XAGp7CMHCTkaqPH/U wHNXAR5XBfvmFteMzl0Gfkz9yOcAs9MGgTfL/HQPfsbeWdK3lJbknAfiXuN2KlV6nJwA nFMrXCiL/I1CbRqC7x3dlbwfrrBL1n3QKsNRlF+FhXNlHIHPrJe9MEdBekD69xvqjuTV UadlbG1wouM4Bibuvq+qRrPIpe6PGWFfr5/INF7ArHo1DuXUzykUN6/jI3iPhIQeZ2R6 /c/c9obU02VVSt+5nF98mo7HfSaBdm1TfC7smc9zIxyHEAqbn69XiZKk5X07FWhp5J+y Obzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-type:content-transfer-encoding; bh=VquFXPHNpLfUvHIT7Ay8v1oK+AnegArNrc6epURKSws=; b=cKyo20fXC1MhXdTx2QPko5I/NgtSZZ8pUSJtfMkMwgUSnVz28/qBaqWz1AyfIV2GFV nRtOlO3NjFy6f0z6FS4FEI07JqBeecIerQQm28B9D3Vg2opggDk38ZSkIEhU5/Rdy4Ti 67vk8gf1vUi1m/nxtnLRK2xDaaTu44hxmLR1RmRso5XbPsigwB+kPkCYRKRfb1R+5MnB 44Ik5PUF+jvIQy+Dptj+uE6yFvfjDZyobmhrnvFro8lcYYakc3q/n8gid8k5yDLiztki 6+wKvmd04U2mCUqd2caa59PuMeaxx5wLSACr2KU+RHo+xtXOUrEqgNY6WK32rY3wIBYV L78Q== X-Gm-Message-State: ALoCoQmftBoMVP1LACDNV6h2D0T+r4apTmCB341vunfpQAVjrfu+1S+F8iEQq7HgYtaDZAUb/uMgyXFF3udfGP2IZuNrhrEvrA== X-Received: by 10.28.54.21 with SMTP id d21mr6046295wma.20.1449599581480; Tue, 08 Dec 2015 10:33:01 -0800 (PST) Received: from zen.linaro.local ([81.128.185.34]) by smtp.gmail.com with ESMTPSA id n7sm4558386wmf.21.2015.12.08.10.32.58 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 08 Dec 2015 10:33:00 -0800 (PST) Received: from zen.linaroharston (localhost [127.0.0.1]) by zen.linaro.local (Postfix) with ESMTP id 00A353E06EF; Tue, 8 Dec 2015 18:32:57 +0000 (GMT) From: =?UTF-8?q?Alex=20Benn=C3=A9e?= To: qemu-devel@nongnu.org, qemu-arm@nongnu.org, peter.maydell@linaro.org, christoffer.dall@linaro.org, zhichao.huang@linaro.org Date: Tue, 8 Dec 2015 18:32:33 +0000 Message-Id: <1449599553-24713-7-git-send-email-alex.bennee@linaro.org> X-Mailer: git-send-email 2.6.3 In-Reply-To: <1449599553-24713-1-git-send-email-alex.bennee@linaro.org> References: <1449599553-24713-1-git-send-email-alex.bennee@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::229 Cc: marc.zyngier@arm.com, =?UTF-8?q?Alex=20Benn=C3=A9e?= , linux-arm-kernel@lists.infradead.org, kvm@vger.kernel.org, kvmarm@lists.cs.columbia.edu Subject: [Qemu-devel] [PATCH v10 6/6] tests/guest-debug: introduce basic gdbstub tests X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patch=linaro.org@nongnu.org Sender: qemu-devel-bounces+patch=linaro.org@nongnu.org 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 --- v10: - fixup for Py2/3 cleanliness - drop to shell on exception --- tests/guest-debug/test-gdbstub.py | 176 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 tests/guest-debug/test-gdbstub.py -- 2.6.3 diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py new file mode 100644 index 0000000..31ba6c9 --- /dev/null +++ b/tests/guest-debug/test-gdbstub.py @@ -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)