summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/selftest/cases/debuginfod.py
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/selftest/cases/debuginfod.py')
-rw-r--r--meta/lib/oeqa/selftest/cases/debuginfod.py153
1 files changed, 128 insertions, 25 deletions
diff --git a/meta/lib/oeqa/selftest/cases/debuginfod.py b/meta/lib/oeqa/selftest/cases/debuginfod.py
index 3c40119282..505b4be837 100644
--- a/meta/lib/oeqa/selftest/cases/debuginfod.py
+++ b/meta/lib/oeqa/selftest/cases/debuginfod.py
@@ -6,50 +6,153 @@
import os
import socketserver
import subprocess
+import time
+import urllib
+import pathlib
+from oeqa.core.decorator import OETestTag
from oeqa.selftest.case import OESelftestTestCase
from oeqa.utils.commands import bitbake, get_bb_var, runqemu
class Debuginfod(OESelftestTestCase):
- def test_debuginfod(self):
- self.write_config(
- """
-DISTRO_FEATURES:append = " debuginfod"
-CORE_IMAGE_EXTRA_INSTALL += "elfutils"
+
+ def wait_for_debuginfod(self, port):
"""
- )
- bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot")
+ debuginfod takes time to scan the packages and requesting too early may
+ result in a test failure if the right packages haven't been scanned yet.
+
+ Request the metrics endpoint periodically and wait for there to be no
+ busy scanning threads.
+
+ Returns if debuginfod is ready, raises an exception if not within the
+ timeout.
+ """
+
+ # Wait two minutes
+ countdown = 24
+ delay = 5
+ latest = None
+
+ while countdown:
+ self.logger.info("waiting...")
+ time.sleep(delay)
+
+ self.logger.info("polling server")
+ if self.debuginfod.poll():
+ self.logger.info("server dead")
+ self.debuginfod.communicate()
+ self.fail("debuginfod terminated unexpectedly")
+ self.logger.info("server alive")
+
+ try:
+ with urllib.request.urlopen("http://localhost:%d/metrics" % port, timeout=10) as f:
+ for line in f.read().decode("ascii").splitlines():
+ key, value = line.rsplit(" ", 1)
+ if key == "thread_busy{role=\"scan\"}":
+ latest = int(value)
+ self.logger.info("Waiting for %d scan jobs to finish" % latest)
+ if latest == 0:
+ return
+ except urllib.error.URLError as e:
+ # TODO: how to catch just timeouts?
+ self.logger.error(e)
+
+ countdown -= 1
+
+ raise TimeoutError("Cannot connect debuginfod, still %d scan jobs running" % latest)
+
+ def start_debuginfod(self):
+ # We assume that the caller has already bitbake'd elfutils-native:do_addto_recipe_sysroot
+
+ # Save some useful paths for later
+ native_sysroot = pathlib.Path(get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native"))
+ native_bindir = native_sysroot / "usr" / "bin"
+ self.debuginfod = native_bindir / "debuginfod"
+ self.debuginfod_find = native_bindir / "debuginfod-find"
- native_sysroot = get_bb_var("RECIPE_SYSROOT_NATIVE", "elfutils-native")
cmd = [
- os.path.join(native_sysroot, "usr", "bin", "debuginfod"),
+ self.debuginfod,
"--verbose",
+ # In-memory database, this is a one-shot test
"--database=:memory:",
+ # Don't use all the host cores
+ "--concurrency=8",
+ "--connection-pool=8",
+ # Disable rescanning, this is a one-shot test
+ "--rescan-time=0",
+ "--groom-time=0",
get_bb_var("DEPLOY_DIR"),
]
- for format in get_bb_var("PACKAGE_CLASSES").split():
- if format == "package_deb":
- cmd.append("--scan-deb-dir")
- elif format == "package_ipk":
- cmd.append("--scan-deb-dir")
- elif format == "package_rpm":
- cmd.append("--scan-rpm-dir")
- # Find a free port
+
+ format = get_bb_var("PACKAGE_CLASSES").split()[0]
+ if format == "package_deb":
+ cmd.append("--scan-deb-dir")
+ elif format == "package_ipk":
+ cmd.append("--scan-deb-dir")
+ elif format == "package_rpm":
+ cmd.append("--scan-rpm-dir")
+ else:
+ self.fail("Unknown package class %s" % format)
+
+ # Find a free port. Racey but the window is small.
with socketserver.TCPServer(("localhost", 0), None) as s:
- port = s.server_address[1]
- cmd.append("--port=%d" % port)
+ self.port = s.server_address[1]
+ cmd.append("--port=%d" % self.port)
+
+ self.logger.info(f"Starting server {cmd}")
+ self.debuginfod = subprocess.Popen(cmd, env={})
+ self.wait_for_debuginfod(self.port)
+
+
+ def test_debuginfod_native(self):
+ """
+ Test debuginfod outside of qemu, by building a package and looking up a
+ binary's debuginfo using elfutils-native.
+ """
+
+ self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-debuginfod"
+DISTRO_FEATURES:append = " debuginfod"
+""")
+ bitbake("elfutils-native:do_addto_recipe_sysroot xz xz:do_package")
+
+ try:
+ self.start_debuginfod()
+
+ env = os.environ.copy()
+ env["DEBUGINFOD_URLS"] = "http://localhost:%d/" % self.port
+
+ pkgs = pathlib.Path(get_bb_var("PKGDEST", "xz"))
+ cmd = (self.debuginfod_find, "debuginfo", pkgs / "xz" / "usr" / "bin" / "xz.xz")
+ self.logger.info(f"Starting client {cmd}")
+ output = subprocess.check_output(cmd, env=env, text=True)
+ # This should be more comprehensive
+ self.assertIn("/.cache/debuginfod_client/", output)
+ finally:
+ self.debuginfod.kill()
+
+ @OETestTag("runqemu")
+ def test_debuginfod_qemu(self):
+ """
+ Test debuginfod-find inside a qemu, talking to a debuginfod on the host.
+ """
+
+ self.write_config("""
+TMPDIR = "${TOPDIR}/tmp-debuginfod"
+DISTRO_FEATURES:append = " debuginfod"
+CORE_IMAGE_EXTRA_INSTALL += "elfutils xz"
+ """)
+ bitbake("core-image-minimal elfutils-native:do_addto_recipe_sysroot")
try:
- debuginfod = subprocess.Popen(cmd)
+ self.start_debuginfod()
with runqemu("core-image-minimal", runqemuparams="nographic") as qemu:
- cmd = (
- "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/debuginfod"
- % (qemu.server_ip, port)
- )
+ cmd = "DEBUGINFOD_URLS=http://%s:%d/ debuginfod-find debuginfo /usr/bin/xz" % (qemu.server_ip, self.port)
+ self.logger.info(f"Starting client {cmd}")
status, output = qemu.run_serial(cmd)
# This should be more comprehensive
self.assertIn("/.cache/debuginfod_client/", output)
finally:
- debuginfod.kill()
+ self.debuginfod.kill()