summaryrefslogtreecommitdiffstats
path: root/meta
diff options
context:
space:
mode:
authorRoss Burton <ross.burton@arm.com>2023-11-21 13:19:24 +0000
committerSteve Sakoman <steve@sakoman.com>2023-11-24 05:01:37 -1000
commit9c1fb1c9ef1f49330ce90ae09ece4548e54ccf2b (patch)
tree60fd631e3f8d853c38a15876c2348319f9bdf5d2 /meta
parent6a35bdf5715de0018f2cec14efade52fdbc8eecb (diff)
downloadpoky-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.py124
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 @@
6import os 6import os
7import socketserver 7import socketserver
8import subprocess 8import subprocess
9import time
10import urllib
11import pathlib
9 12
13from oeqa.core.decorator import OETestTag
10from oeqa.selftest.case import OESelftestTestCase 14from oeqa.selftest.case import OESelftestTestCase
11from oeqa.utils.commands import bitbake, get_bb_var, runqemu 15from 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
48DISTRO_FEATURES:append = " debuginfod" 68 # Save some useful paths for later
49CORE_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("""
115TMPDIR = "${TOPDIR}/tmp-debuginfod"
116DISTRO_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("""
142TMPDIR = "${TOPDIR}/tmp-debuginfod"
143DISTRO_FEATURES:append = " debuginfod"
144CORE_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()