diff options
-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 | |||