oeqa/selftest: rewrite gdbserver test

The gdbserver test case didn't actually work and doesn't follow the
documentation for how to use gdbserver in Yocto.  Rewrite the test case
to follow the documented process so if that breaks then we're aware.

(From OE-Core rev: a8eddb71b16a2b958cde54d0dbd35f7a9467ddd2)

Signed-off-by: Ross Burton <ross.burton@arm.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Ross Burton
2022-08-22 16:19:50 +01:00
committed by Richard Purdie
parent e015c6cf32
commit 238660fcca

View File

@@ -4,122 +4,63 @@
# SPDX-License-Identifier: MIT
#
import os
from subprocess import Popen, PIPE
import threading
import time
import tempfile
import shutil
import concurrent.futures
from oeqa.selftest.case import OESelftestTestCase
from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runqemu , runCmd
from oeqa.core.exception import OEQATimeoutError
#The test runs gdbserver on qemu and connects the gdb client from host over TCP.
#
#It builds a cross gdb on the host and compiles the program to be debugged on the target,
#launches the gdbserver and tries to connect cross gdb to it.
class GdbTest(OESelftestTestCase):
def run_gdb(self, native_sysroot=None, **options):
# Add path to cross gdb to environment.
extra_paths = "%s/usr/bin/%s" % (self.recipe_sysroot_native, self.target_sys)
nenv = dict(options.get('env', os.environ))
nenv['PATH'] = extra_paths + ':' + nenv.get('PATH', '')
options['env'] = nenv
for count in range(5):
# Let the gdb server start before by putting client thread to sleep.
# Still, if gdb client happens to start before gdbserver, if will
# return "gdb connection timed out". In that case try
# connecting again
time.sleep(1)
cmd = "%s-gdb -ex 'set sysroot %s' -ex \"target remote %s:%s\" -ex continue -ex quit %s" \
%(self.target_sys, self.recipe_sysroot_native, self.qemu_ip, self.gdbserver_port, self.binary)
r = runCmd(cmd, native_sysroot=self.recipe_sysroot_native, **options)
if "Connection timed out" not in r.output:
break
if count == 4:
self.assertTrue(False, "gdb unable to connect to gdbserver")
def run_gdb_client(self):
self.run_gdb(native_sysroot=self.recipe_sysroot_native, ignore_status=False)
from oeqa.utils.commands import bitbake, get_bb_var, runqemu, runCmd
class GdbServerTest(OESelftestTestCase):
def test_gdb_server(self):
self.target_dst = "/tmp/"
self.source = "test.c"
self.binary = "test"
self.target_source = self.target_dst + self.source
self.target_binary = self.target_dst + self.binary
self.gdbserver_port = 2001
target_arch = self.td["TARGET_ARCH"]
target_sys = self.td["TARGET_SYS"]
deploy_dir = get_bb_var("DEPLOY_DIR_IMAGE")
try:
# These aren't the actual IP addresses but testexport class needs something defined
features = 'TEST_SERVER_IP = "192.168.7.1"\n'
features += 'TEST_TARGET_IP = "192.168.7.2"\n'
features += 'EXTRA_IMAGE_FEATURES += "ssh-server-openssh"\n'
features += 'CORE_IMAGE_EXTRA_INSTALL += "gdbserver packagegroup-core-buildessential"\n'
self.write_config(features)
features = """
IMAGE_GEN_DEBUGFS = "1"
IMAGE_FSTYPES_DEBUGFS = "tar.bz2"
CORE_IMAGE_EXTRA_INSTALL = "gdbserver"
"""
self.write_config(features)
self.target_arch = get_bb_var('TARGET_ARCH')
self.target_sys = get_bb_var('TARGET_SYS')
gdb_recipe = "gdb-cross-" + target_arch
gdb_binary = target_sys + "-gdb"
recipe = "gdb-cross-%s" %self.target_arch
gdb_cross_bitbake_command = "%s -c addto_recipe_sysroot" %recipe
bitbake("core-image-minimal %s:do_addto_recipe_sysroot" % gdb_recipe)
bitbake(gdb_cross_bitbake_command)
native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe)
r = runCmd("%s --version" % gdb_binary, native_sysroot=native_sysroot, target_sys=target_sys)
self.assertEqual(r.status, 0)
self.assertIn("GNU gdb", r.output)
self.recipe_sysroot_native = get_bb_var('RECIPE_SYSROOT_NATIVE', recipe)
with tempfile.TemporaryDirectory(prefix="debugfs-") as debugfs:
filename = os.path.join(deploy_dir, "core-image-minimal-%s-dbg.tar.bz2" % self.td["MACHINE"])
shutil.unpack_archive(filename, debugfs)
filename = os.path.join(deploy_dir, "core-image-minimal-%s.tar.bz2" % self.td["MACHINE"])
shutil.unpack_archive(filename, debugfs)
bitbake('core-image-minimal')
with runqemu("core-image-minimal", runqemuparams="nographic") as qemu:
status, output = qemu.run_serial("kmod --help")
self.assertIn("modprobe", output)
self.cross_gdb_name = "%s-gdb" %self.target_sys
# wrap the execution with a qemu instance
with runqemu("core-image-minimal", runqemuparams = "nographic") as qemu:
status, target_gcc = qemu.run_serial("which gcc")
self.assertNotEqual(target_gcc, None, 'gcc not found on the target')
self.qemu_ip = qemu.ip
# self.tc.files_dir = meta/lib/oeqa/files
src = os.path.join(self.tc.files_dir, 'test.c')
self.logger.debug("src : %s qemu.ip : %s self.target_dst : %s" % (src , self.qemu_ip , self.target_dst))
cmd = "scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR %s root@%s:%s" % (src, qemu.ip, self.target_dst)
result = runCmd(cmd)
cmd = "cat %s" %(self.target_source)
status, output = qemu.run_serial(cmd)
self.assertIn("1234", output, "Source file copied to target is different that what is expected")
cmd = "%s %s -o %s -lm -g" % (target_gcc, self.target_source, self.target_binary)
status, output = qemu.run_serial(cmd)
cmd = "ls %s" %self.target_binary
status, output = qemu.run_serial(cmd)
self.assertNotIn("No such file or directory", output, "Test file compilation failed on target")
status, output = qemu.run_serial(self.target_binary)
self.assertIn("1234", output, "Test binary is different than what is expected")
cmd = "scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR root@%s:%s %s" % (qemu.ip, self.target_binary, os.getcwd())
result = runCmd(cmd)
cmd = "strip -s --strip-debug %s" %self.target_binary
status, output = qemu.run_serial(cmd)
gdb_client_thread = threading.Thread(target=self.run_gdb_client)
gdb_client_thread.start()
cmd = "gdbserver localhost:%s %s" %(self.gdbserver_port, self.target_binary)
status, output = qemu.run_serial(cmd)
self.logger.debug("gdbserver status : %s output : %s" % (status, output))
gdb_client_thread.join()
self.assertIn("1234", output, "Expected string (1234) not present in test output")
except OEQATimeoutError:
self.fail("gdbserver test timeout error")
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
def run_gdb():
for _ in range(5):
time.sleep(2)
cmd = "%s --batch -ex 'set sysroot %s' -ex \"target extended-remote %s:9999\" -ex \"info line kmod_help\"" % (gdb_binary, debugfs, qemu.ip)
self.logger.warning("starting gdb %s" % cmd)
r = runCmd(cmd, native_sysroot=native_sysroot, target_sys=target_sys)
self.assertEqual(0, r.status)
line_re = r"Line \d+ of \"/usr/src/debug/kmod/.*/tools/kmod.c\" starts at address 0x[0-9A-Fa-f]+ <kmod_help>"
self.assertRegex(r.output, line_re)
break
else:
self.fail("Timed out connecting to gdb")
future = executor.submit(run_gdb)
status, output = qemu.run_serial("gdbserver --once :9999 kmod --help")
self.assertEqual(status, 1)
# The future either returns None, or raises an exception
future.result()