diff options
author | Ross Burton <ross.burton@arm.com> | 2022-08-22 16:19:50 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2022-08-23 15:24:11 +0100 |
commit | 238660fccab248b420c5541e36270d12739b83cf (patch) | |
tree | 26edeef5b4c359928565cb94f21b6561eb3ca172 | |
parent | e015c6cf32e68179d1bb68dfa1d7b72c2447a70b (diff) | |
download | poky-238660fccab248b420c5541e36270d12739b83cf.tar.gz |
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>
-rw-r--r-- | meta/lib/oeqa/selftest/cases/gdbserver.py | 169 |
1 files changed, 55 insertions, 114 deletions
diff --git a/meta/lib/oeqa/selftest/cases/gdbserver.py b/meta/lib/oeqa/selftest/cases/gdbserver.py index 3124902543..3621d9c13e 100644 --- a/meta/lib/oeqa/selftest/cases/gdbserver.py +++ b/meta/lib/oeqa/selftest/cases/gdbserver.py | |||
@@ -4,122 +4,63 @@ | |||
4 | # SPDX-License-Identifier: MIT | 4 | # SPDX-License-Identifier: MIT |
5 | # | 5 | # |
6 | import os | 6 | import os |
7 | from subprocess import Popen, PIPE | ||
8 | import threading | ||
9 | import time | 7 | import time |
8 | import tempfile | ||
9 | import shutil | ||
10 | import concurrent.futures | ||
10 | 11 | ||
11 | from oeqa.selftest.case import OESelftestTestCase | 12 | from oeqa.selftest.case import OESelftestTestCase |
12 | from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runqemu , runCmd | 13 | from oeqa.utils.commands import bitbake, get_bb_var, runqemu, runCmd |
13 | from oeqa.core.exception import OEQATimeoutError | ||
14 | |||
15 | |||
16 | #The test runs gdbserver on qemu and connects the gdb client from host over TCP. | ||
17 | # | ||
18 | #It builds a cross gdb on the host and compiles the program to be debugged on the target, | ||
19 | #launches the gdbserver and tries to connect cross gdb to it. | ||
20 | |||
21 | |||
22 | class GdbTest(OESelftestTestCase): | ||
23 | |||
24 | def run_gdb(self, native_sysroot=None, **options): | ||
25 | # Add path to cross gdb to environment. | ||
26 | extra_paths = "%s/usr/bin/%s" % (self.recipe_sysroot_native, self.target_sys) | ||
27 | nenv = dict(options.get('env', os.environ)) | ||
28 | nenv['PATH'] = extra_paths + ':' + nenv.get('PATH', '') | ||
29 | options['env'] = nenv | ||
30 | |||
31 | for count in range(5): | ||
32 | # Let the gdb server start before by putting client thread to sleep. | ||
33 | # Still, if gdb client happens to start before gdbserver, if will | ||
34 | # return "gdb connection timed out". In that case try | ||
35 | # connecting again | ||
36 | time.sleep(1) | ||
37 | cmd = "%s-gdb -ex 'set sysroot %s' -ex \"target remote %s:%s\" -ex continue -ex quit %s" \ | ||
38 | %(self.target_sys, self.recipe_sysroot_native, self.qemu_ip, self.gdbserver_port, self.binary) | ||
39 | r = runCmd(cmd, native_sysroot=self.recipe_sysroot_native, **options) | ||
40 | if "Connection timed out" not in r.output: | ||
41 | break | ||
42 | if count == 4: | ||
43 | self.assertTrue(False, "gdb unable to connect to gdbserver") | ||
44 | |||
45 | def run_gdb_client(self): | ||
46 | self.run_gdb(native_sysroot=self.recipe_sysroot_native, ignore_status=False) | ||
47 | 14 | ||
15 | class GdbServerTest(OESelftestTestCase): | ||
48 | def test_gdb_server(self): | 16 | def test_gdb_server(self): |
49 | self.target_dst = "/tmp/" | 17 | target_arch = self.td["TARGET_ARCH"] |
50 | self.source = "test.c" | 18 | target_sys = self.td["TARGET_SYS"] |
51 | self.binary = "test" | 19 | deploy_dir = get_bb_var("DEPLOY_DIR_IMAGE") |
52 | self.target_source = self.target_dst + self.source | 20 | |
53 | self.target_binary = self.target_dst + self.binary | 21 | features = """ |
54 | self.gdbserver_port = 2001 | 22 | IMAGE_GEN_DEBUGFS = "1" |
55 | 23 | IMAGE_FSTYPES_DEBUGFS = "tar.bz2" | |
56 | try: | 24 | CORE_IMAGE_EXTRA_INSTALL = "gdbserver" |
57 | # These aren't the actual IP addresses but testexport class needs something defined | 25 | """ |
58 | features = 'TEST_SERVER_IP = "192.168.7.1"\n' | 26 | self.write_config(features) |
59 | features += 'TEST_TARGET_IP = "192.168.7.2"\n' | 27 | |
60 | features += 'EXTRA_IMAGE_FEATURES += "ssh-server-openssh"\n' | 28 | gdb_recipe = "gdb-cross-" + target_arch |
61 | features += 'CORE_IMAGE_EXTRA_INSTALL += "gdbserver packagegroup-core-buildessential"\n' | 29 | gdb_binary = target_sys + "-gdb" |
62 | self.write_config(features) | 30 | |
63 | 31 | bitbake("core-image-minimal %s:do_addto_recipe_sysroot" % gdb_recipe) | |
64 | self.target_arch = get_bb_var('TARGET_ARCH') | 32 | |
65 | self.target_sys = get_bb_var('TARGET_SYS') | 33 | native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", gdb_recipe) |
66 | 34 | r = runCmd("%s --version" % gdb_binary, native_sysroot=native_sysroot, target_sys=target_sys) | |
67 | recipe = "gdb-cross-%s" %self.target_arch | 35 | self.assertEqual(r.status, 0) |
68 | gdb_cross_bitbake_command = "%s -c addto_recipe_sysroot" %recipe | 36 | self.assertIn("GNU gdb", r.output) |
69 | 37 | ||
70 | bitbake(gdb_cross_bitbake_command) | 38 | with tempfile.TemporaryDirectory(prefix="debugfs-") as debugfs: |
71 | 39 | filename = os.path.join(deploy_dir, "core-image-minimal-%s-dbg.tar.bz2" % self.td["MACHINE"]) | |
72 | self.recipe_sysroot_native = get_bb_var('RECIPE_SYSROOT_NATIVE', recipe) | 40 | shutil.unpack_archive(filename, debugfs) |
73 | 41 | filename = os.path.join(deploy_dir, "core-image-minimal-%s.tar.bz2" % self.td["MACHINE"]) | |
74 | bitbake('core-image-minimal') | 42 | shutil.unpack_archive(filename, debugfs) |
75 | 43 | ||
76 | self.cross_gdb_name = "%s-gdb" %self.target_sys | 44 | with runqemu("core-image-minimal", runqemuparams="nographic") as qemu: |
77 | 45 | status, output = qemu.run_serial("kmod --help") | |
78 | # wrap the execution with a qemu instance | 46 | self.assertIn("modprobe", output) |
79 | with runqemu("core-image-minimal", runqemuparams = "nographic") as qemu: | 47 | |
80 | status, target_gcc = qemu.run_serial("which gcc") | 48 | with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor: |
81 | 49 | def run_gdb(): | |
82 | self.assertNotEqual(target_gcc, None, 'gcc not found on the target') | 50 | for _ in range(5): |
83 | 51 | time.sleep(2) | |
84 | self.qemu_ip = qemu.ip | 52 | cmd = "%s --batch -ex 'set sysroot %s' -ex \"target extended-remote %s:9999\" -ex \"info line kmod_help\"" % (gdb_binary, debugfs, qemu.ip) |
85 | 53 | self.logger.warning("starting gdb %s" % cmd) | |
86 | # self.tc.files_dir = meta/lib/oeqa/files | 54 | r = runCmd(cmd, native_sysroot=native_sysroot, target_sys=target_sys) |
87 | src = os.path.join(self.tc.files_dir, 'test.c') | 55 | self.assertEqual(0, r.status) |
88 | 56 | line_re = r"Line \d+ of \"/usr/src/debug/kmod/.*/tools/kmod.c\" starts at address 0x[0-9A-Fa-f]+ <kmod_help>" | |
89 | self.logger.debug("src : %s qemu.ip : %s self.target_dst : %s" % (src , self.qemu_ip , self.target_dst)) | 57 | self.assertRegex(r.output, line_re) |
90 | cmd = "scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR %s root@%s:%s" % (src, qemu.ip, self.target_dst) | 58 | break |
91 | result = runCmd(cmd) | 59 | else: |
92 | 60 | self.fail("Timed out connecting to gdb") | |
93 | cmd = "cat %s" %(self.target_source) | 61 | future = executor.submit(run_gdb) |
94 | status, output = qemu.run_serial(cmd) | 62 | |
95 | self.assertIn("1234", output, "Source file copied to target is different that what is expected") | 63 | status, output = qemu.run_serial("gdbserver --once :9999 kmod --help") |
96 | 64 | self.assertEqual(status, 1) | |
97 | cmd = "%s %s -o %s -lm -g" % (target_gcc, self.target_source, self.target_binary) | 65 | # The future either returns None, or raises an exception |
98 | status, output = qemu.run_serial(cmd) | 66 | future.result() |
99 | |||
100 | cmd = "ls %s" %self.target_binary | ||
101 | status, output = qemu.run_serial(cmd) | ||
102 | self.assertNotIn("No such file or directory", output, "Test file compilation failed on target") | ||
103 | |||
104 | status, output = qemu.run_serial(self.target_binary) | ||
105 | self.assertIn("1234", output, "Test binary is different than what is expected") | ||
106 | |||
107 | cmd = "scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR root@%s:%s %s" % (qemu.ip, self.target_binary, os.getcwd()) | ||
108 | result = runCmd(cmd) | ||
109 | |||
110 | cmd = "strip -s --strip-debug %s" %self.target_binary | ||
111 | status, output = qemu.run_serial(cmd) | ||
112 | |||
113 | gdb_client_thread = threading.Thread(target=self.run_gdb_client) | ||
114 | gdb_client_thread.start() | ||
115 | |||
116 | cmd = "gdbserver localhost:%s %s" %(self.gdbserver_port, self.target_binary) | ||
117 | status, output = qemu.run_serial(cmd) | ||
118 | self.logger.debug("gdbserver status : %s output : %s" % (status, output)) | ||
119 | |||
120 | gdb_client_thread.join() | ||
121 | self.assertIn("1234", output, "Expected string (1234) not present in test output") | ||
122 | |||
123 | except OEQATimeoutError: | ||
124 | self.fail("gdbserver test timeout error") | ||
125 | |||