diff options
author | Ross Burton <ross.burton@arm.com> | 2023-11-21 13:19:24 +0000 |
---|---|---|
committer | Steve Sakoman <steve@sakoman.com> | 2023-11-24 05:01:37 -1000 |
commit | 9c1fb1c9ef1f49330ce90ae09ece4548e54ccf2b (patch) | |
tree | 60fd631e3f8d853c38a15876c2348319f9bdf5d2 /meta | |
parent | 6a35bdf5715de0018f2cec14efade52fdbc8eecb (diff) | |
download | poky-9c1fb1c9ef1f49330ce90ae09ece4548e54ccf2b.tar.gz |
oeqa/selftest/debuginfod: improve selftest
This test was occasionally failing for no obvious reason, so refactor
and improve:
- While waiting for the daemon, check that it is still running and
explicitly timeout after 10s when making the HTTP call.
- While waiting for the daemon to be ready, log the current state of the
daemon so we can tell if we're timing out as it is still scanning.
- This was in fact the cause of the intermittant failures, because the
TMPDIR is reused between tests and may contain a large number of
packages. Do the tests in an isolated TMPDIR to hopefully mitigate this
issue and increase the timeout to two minutes.
- Decorate the test using runqemu as such so that can be skipped in
environments without runqemu
- Add a second test that doesn't use runqemu or images, which is faster
but less realistic.
(From OE-Core rev: 99590fac1bfb5474f5bf0e02d3888b518af9fb3e)
Signed-off-by: Ross Burton <ross.burton@arm.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
(cherry picked from commit 88b660aaae2527736b6eccec4c952eee969e20a2)
Signed-off-by: Steve Sakoman <steve@sakoman.com>
Diffstat (limited to 'meta')
-rw-r--r-- | meta/lib/oeqa/selftest/cases/debuginfod.py | 124 |
1 files changed, 88 insertions, 36 deletions
diff --git a/meta/lib/oeqa/selftest/cases/debuginfod.py b/meta/lib/oeqa/selftest/cases/debuginfod.py index 37f51760fb..505b4be837 100644 --- a/meta/lib/oeqa/selftest/cases/debuginfod.py +++ b/meta/lib/oeqa/selftest/cases/debuginfod.py | |||
@@ -6,7 +6,11 @@ | |||
6 | import os | 6 | import os |
7 | import socketserver | 7 | import socketserver |
8 | import subprocess | 8 | import subprocess |
9 | import time | ||
10 | import urllib | ||
11 | import pathlib | ||
9 | 12 | ||
13 | from oeqa.core.decorator import OETestTag | ||
10 | from oeqa.selftest.case import OESelftestTestCase | 14 | from oeqa.selftest.case import OESelftestTestCase |
11 | from oeqa.utils.commands import bitbake, get_bb_var, runqemu | 15 | from oeqa.utils.commands import bitbake, get_bb_var, runqemu |
12 | 16 | ||
@@ -21,39 +25,54 @@ class Debuginfod(OESelftestTestCase): | |||
21 | Request the metrics endpoint periodically and wait for there to be no | 25 | Request the metrics endpoint periodically and wait for there to be no |
22 | busy scanning threads. | 26 | busy scanning threads. |
23 | 27 | ||
24 | Returns True if debuginfod is ready, False if we timed out | 28 | Returns if debuginfod is ready, raises an exception if not within the |
29 | timeout. | ||
25 | """ | 30 | """ |
26 | import time, urllib | ||
27 | 31 | ||
28 | # Wait a minute | 32 | # Wait two minutes |
29 | countdown = 6 | 33 | countdown = 24 |
30 | delay = 10 | 34 | delay = 5 |
35 | latest = None | ||
31 | 36 | ||
32 | while countdown: | 37 | while countdown: |
38 | self.logger.info("waiting...") | ||
33 | time.sleep(delay) | 39 | time.sleep(delay) |
40 | |||
41 | self.logger.info("polling server") | ||
42 | if self.debuginfod.poll(): | ||
43 | self.logger.info("server dead") | ||
44 | self.debuginfod.communicate() | ||
45 | self.fail("debuginfod terminated unexpectedly") | ||
46 | self.logger.info("server alive") | ||
47 | |||
34 | try: | 48 | try: |
35 | with urllib.request.urlopen("http://localhost:%d/metrics" % port) as f: | 49 | with urllib.request.urlopen("http://localhost:%d/metrics" % port, timeout=10) as f: |
36 | lines = f.read().decode("ascii").splitlines() | 50 | for line in f.read().decode("ascii").splitlines(): |
37 | if "thread_busy{role=\"scan\"} 0" in lines: | 51 | key, value = line.rsplit(" ", 1) |
38 | return True | 52 | if key == "thread_busy{role=\"scan\"}": |
53 | latest = int(value) | ||
54 | self.logger.info("Waiting for %d scan jobs to finish" % latest) | ||
55 | if latest == 0: | ||
56 | return | ||
39 | except urllib.error.URLError as e: | 57 | except urllib.error.URLError as e: |
58 | # TODO: how to catch just timeouts? | ||
40 | self.logger.error(e) | 59 | self.logger.error(e) |
60 | |||
41 | countdown -= 1 | 61 | countdown -= 1 |
42 | return False | ||
43 | 62 | ||
63 | raise TimeoutError("Cannot connect debuginfod, still %d scan jobs running" % latest) | ||
44 | 64 | ||
45 | def test_debuginfod(self): | 65 | def start_debuginfod(self): |
46 | self.write_config( | 66 | # We assume that the caller has already bitbake'd elfutils-native:do_addto_recipe_sysroot |
47 | """ | 67 | |
48 | DISTRO_FEATURES:append = " debuginfod" | 68 | # Save some useful paths for later |
49 | CORE_IMAGE_EXTRA_INSTALL += "elfutils" | 69 | native_sysroot = pathlib.Path(get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native")) |
50 | """ | 70 | native_bindir = native_sysroot / "usr" / "bin" |
51 | ) | 71 | self.debuginfod = native_bindir / "debuginfod" |
52 | bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot") | 72 | self.debuginfod_find = native_bindir / "debuginfod-find" |
53 | 73 | ||
54 | native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native") | ||
55 | cmd = [ | 74 | cmd = [ |
56 | os.path.join(native_sysroot, "usr", "bin", "debuginfod"), | 75 | self.debuginfod, |
57 | "--verbose", | 76 | "--verbose", |
58 | # In-memory database, this is a one-shot test | 77 | # In-memory database, this is a one-shot test |
59 | "--database=:memory:", | 78 | "--database=:memory:", |
@@ -76,31 +95,64 @@ CORE_IMAGE_EXTRA_INSTALL += "elfutils" | |||
76 | else: | 95 | else: |
77 | self.fail("Unknown package class %s" % format) | 96 | self.fail("Unknown package class %s" % format) |
78 | 97 | ||
79 | # Find a free port | 98 | # Find a free port. Racey but the window is small. |
80 | with socketserver.TCPServer(("localhost", 0), None) as s: | 99 | with socketserver.TCPServer(("localhost", 0), None) as s: |
81 | port = s.server_address[1] | 100 | self.port = s.server_address[1] |
82 | cmd.append("--port=%d" % port) | 101 | cmd.append("--port=%d" % self.port) |
102 | |||
103 | self.logger.info(f"Starting server {cmd}") | ||
104 | self.debuginfod = subprocess.Popen(cmd, env={}) | ||
105 | self.wait_for_debuginfod(self.port) | ||
106 | |||
107 | |||
108 | def test_debuginfod_native(self): | ||
109 | """ | ||
110 | Test debuginfod outside of qemu, by building a package and looking up a | ||
111 | binary's debuginfo using elfutils-native. | ||
112 | """ | ||
113 | |||
114 | self.write_config(""" | ||
115 | TMPDIR = "${TOPDIR}/tmp-debuginfod" | ||
116 | DISTRO_FEATURES:append = " debuginfod" | ||
117 | """) | ||
118 | bitbake("elfutils-native:do_addto_recipe_sysroot xz xz:do_package") | ||
83 | 119 | ||
84 | try: | 120 | try: |
85 | # Remove DEBUGINFOD_URLS from the environment so we don't try | 121 | self.start_debuginfod() |
86 | # looking in the distro debuginfod | 122 | |
87 | env = os.environ.copy() | 123 | env = os.environ.copy() |
88 | if "DEBUGINFOD_URLS" in env: | 124 | env["DEBUGINFOD_URLS"] = "http://localhost:%d/" % self.port |
89 | del env["DEBUGINFOD_URLS"] | 125 | |
126 | pkgs = pathlib.Path(get_bb_var("PKGDEST", "xz")) | ||
127 | cmd = (self.debuginfod_find, "debuginfo", pkgs / "xz" / "usr" / "bin" / "xz.xz") | ||
128 | self.logger.info(f"Starting client {cmd}") | ||
129 | output = subprocess.check_output(cmd, env=env, text=True) | ||
130 | # This should be more comprehensive | ||
131 | self.assertIn("/.cache/debuginfod_client/", output) | ||
132 | finally: | ||
133 | self.debuginfod.kill() | ||
134 | |||
135 | @OETestTag("runqemu") | ||
136 | def test_debuginfod_qemu(self): | ||
137 | """ | ||
138 | Test debuginfod-find inside a qemu, talking to a debuginfod on the host. | ||
139 | """ | ||
140 | |||
141 | self.write_config(""" | ||
142 | TMPDIR = "${TOPDIR}/tmp-debuginfod" | ||
143 | DISTRO_FEATURES:append = " debuginfod" | ||
144 | CORE_IMAGE_EXTRA_INSTALL += "elfutils xz" | ||
145 | """) | ||
146 | bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot") | ||
90 | 147 | ||
91 | self.logger.info(f"Starting server {cmd}") | 148 | try: |
92 | debuginfod = subprocess.Popen(cmd, env=env) | 149 | self.start_debuginfod() |
93 | 150 | ||
94 | with runqemu("core-image-minimal", runqemuparams="nographic") as qemu: | 151 | with runqemu("core-image-minimal", runqemuparams="nographic") as qemu: |
95 | self.assertTrue(self.wait_for_debuginfod(port)) | 152 | cmd = "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/xz" % (qemu.server_ip, self.port) |
96 | |||
97 | cmd = ( | ||
98 | "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/debuginfod" | ||
99 | % (qemu.server_ip, port) | ||
100 | ) | ||
101 | self.logger.info(f"Starting client {cmd}") | 153 | self.logger.info(f"Starting client {cmd}") |
102 | status, output = qemu.run_serial(cmd) | 154 | status, output = qemu.run_serial(cmd) |
103 | # This should be more comprehensive | 155 | # This should be more comprehensive |
104 | self.assertIn("/.cache/debuginfod_client/", output) | 156 | self.assertIn("/.cache/debuginfod_client/", output) |
105 | finally: | 157 | finally: |
106 | debuginfod.kill() | 158 | self.debuginfod.kill() |