diff mbox series

[3/3] oeqa/selftest: rewrite gdbserver test

Message ID 20220822151950.2611880-3-ross.burton@arm.com
State Accepted, archived
Commit a8eddb71b16a2b958cde54d0dbd35f7a9467ddd2
Headers show
Series [1/3] oeqa/qemurunner: add run_serial() comment | expand

Commit Message

Ross Burton Aug. 22, 2022, 3:19 p.m. UTC
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.

Signed-off-by: Ross Burton <ross.burton@arm.com>
---
 meta/lib/oeqa/selftest/cases/gdbserver.py | 169 +++++++---------------
 1 file changed, 55 insertions(+), 114 deletions(-)
diff mbox series

Patch

diff --git a/meta/lib/oeqa/selftest/cases/gdbserver.py b/meta/lib/oeqa/selftest/cases/gdbserver.py
index 3124902543c..3621d9c13eb 100644
--- a/meta/lib/oeqa/selftest/cases/gdbserver.py
+++ b/meta/lib/oeqa/selftest/cases/gdbserver.py
@@ -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
-
-        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)
-
-            self.target_arch = get_bb_var('TARGET_ARCH')
-            self.target_sys  = get_bb_var('TARGET_SYS')
-
-            recipe = "gdb-cross-%s" %self.target_arch
-            gdb_cross_bitbake_command = "%s -c addto_recipe_sysroot" %recipe
-
-            bitbake(gdb_cross_bitbake_command)
-
-            self.recipe_sysroot_native = get_bb_var('RECIPE_SYSROOT_NATIVE', recipe)
-
-            bitbake('core-image-minimal')
-
-            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")
-
+        target_arch = self.td["TARGET_ARCH"]
+        target_sys = self.td["TARGET_SYS"]
+        deploy_dir = get_bb_var("DEPLOY_DIR_IMAGE")
+
+        features = """
+IMAGE_GEN_DEBUGFS = "1"
+IMAGE_FSTYPES_DEBUGFS = "tar.bz2"
+CORE_IMAGE_EXTRA_INSTALL = "gdbserver"
+        """
+        self.write_config(features)
+
+        gdb_recipe = "gdb-cross-" + target_arch
+        gdb_binary = target_sys + "-gdb"
+
+        bitbake("core-image-minimal %s:do_addto_recipe_sysroot" % gdb_recipe)
+
+        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)
+
+        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)
+
+            with runqemu("core-image-minimal", runqemuparams="nographic") as qemu:
+                status, output = qemu.run_serial("kmod --help")
+                self.assertIn("modprobe", output)
+
+                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()