aboutsummaryrefslogtreecommitdiff
path: root/lldb/packages/Python/lldbsuite
diff options
context:
space:
mode:
authorJulian Lettner <yln@users.noreply.github.com>2025-09-24 13:31:02 -0700
committerGitHub <noreply@github.com>2025-09-24 13:31:02 -0700
commit5d511b6736d0c70a99ea38bd800be0ffe65991e7 (patch)
treee25724092ba334c329207f02ce3d3d0a58a4b704 /lldb/packages/Python/lldbsuite
parentd235d62d65a8403f1a239720058646bcefcd3d74 (diff)
downloadllvm-5d511b6736d0c70a99ea38bd800be0ffe65991e7.zip
llvm-5d511b6736d0c70a99ea38bd800be0ffe65991e7.tar.gz
llvm-5d511b6736d0c70a99ea38bd800be0ffe65991e7.tar.bz2
[lldb][test] Unify test infrastructure for checking CPU features (#153914)
This addresses limitations in our testing infrastructure for checking CPU features. Before this * `getCPUInfo()` was Linux-only, and the * `@skipUnlessFeature` decorator was Darwin-only and did not consider the remote (on device) testing use case. Introduce `CPUFeature` class as an abstraction to hide the platform-specific implementations to check for CPU features. Unify local (on host) and remote (on device) test execution by always going through `test.run_platform_command()` which uses LLDB's `platform shell <cmd>` command. Potential future cleanups: I think `@skipUnlessFeature` decorator could be used in place of code like this: ``` if not self.isAArch64SME(): self.skipTest("SME must be present.") if not self.isAArch64SME2(): self.skipTest("SME2 must be present.") ```
Diffstat (limited to 'lldb/packages/Python/lldbsuite')
-rw-r--r--lldb/packages/Python/lldbsuite/test/cpu_feature.py75
-rw-r--r--lldb/packages/Python/lldbsuite/test/decorators.py24
-rw-r--r--lldb/packages/Python/lldbsuite/test/lldbtest.py66
3 files changed, 103 insertions, 62 deletions
diff --git a/lldb/packages/Python/lldbsuite/test/cpu_feature.py b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
new file mode 100644
index 0000000..3f43cbb
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/cpu_feature.py
@@ -0,0 +1,75 @@
+"""
+Platform-agnostic helper to query for CPU features.
+"""
+
+import re
+
+
+class CPUFeature:
+ def __init__(self, linux_cpu_info_flag: str = None, darwin_sysctl_key: str = None):
+ self.cpu_info_flag = linux_cpu_info_flag
+ self.sysctl_key = darwin_sysctl_key
+
+ def __str__(self):
+ for arch_class in ALL_ARCHS:
+ for feat_var in dir(arch_class):
+ if self == getattr(arch_class, feat_var):
+ return f"{arch_class.__name__}.{feat_var}"
+ raise AssertionError("unreachable")
+
+ def is_supported(self, triple, cmd_runner):
+ if re.match(".*-.*-linux", triple):
+ err_msg, res = self._is_supported_linux(cmd_runner)
+ elif re.match(".*-apple-.*", triple):
+ err_msg, res = self._is_supported_darwin(cmd_runner)
+ else:
+ err_msg, res = None, False
+
+ if err_msg:
+ print(f"CPU feature check failed: {err_msg}")
+
+ return res
+
+ def _is_supported_linux(self, cmd_runner):
+ if not self.cpu_info_flag:
+ return f"Unspecified cpuinfo flag for {self}", False
+
+ cmd = "cat /proc/cpuinfo"
+ err, retcode, output = cmd_runner(cmd)
+ if err.Fail() or retcode != 0:
+ return output, False
+
+ # FIXME: simple substring match, e.g., test for 'sme' will be true if
+ # 'sme2' or 'smefa64' is present
+ return None, (self.cpu_info_flag in output)
+
+ def _is_supported_darwin(self, cmd_runner):
+ if not self.sysctl_key:
+ return f"Unspecified sysctl key for {self}", False
+
+ cmd = f"sysctl -n {self.sysctl_key}"
+ err, retcode, output = cmd_runner(cmd)
+ if err.Fail() or retcode != 0:
+ return output, False
+
+ return None, (output.strip() == "1")
+
+
+class AArch64:
+ FPMR = CPUFeature("fpmr")
+ GCS = CPUFeature("gcs")
+ MTE = CPUFeature("mte")
+ MTE_STORE_ONLY = CPUFeature("mtestoreonly")
+ PTR_AUTH = CPUFeature("paca", "hw.optional.arm.FEAT_PAuth2")
+ SME = CPUFeature("sme", "hw.optional.arm.FEAT_SME")
+ SME_FA64 = CPUFeature("smefa64")
+ SME2 = CPUFeature("sme2", "hw.optional.arm.FEAT_SME2")
+ SVE = CPUFeature("sve")
+
+
+class Loong:
+ LASX = CPUFeature("lasx")
+ LSX = CPUFeature("lsx")
+
+
+ALL_ARCHS = [AArch64, Loong]
diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py
index 16a58cf..454196e 100644
--- a/lldb/packages/Python/lldbsuite/test/decorators.py
+++ b/lldb/packages/Python/lldbsuite/test/decorators.py
@@ -27,6 +27,7 @@ from lldbsuite.support import funcutils
from lldbsuite.support import temp_file
from lldbsuite.test import lldbplatform
from lldbsuite.test import lldbplatformutil
+from lldbsuite.test.cpu_feature import CPUFeature
class DecorateMode:
@@ -1131,24 +1132,13 @@ def skipIfLLVMTargetMissing(target):
return unittest.skipIf(not found, "requires " + target)
-# Call sysctl on darwin to see if a specified hardware feature is available on this machine.
-def skipUnlessFeature(feature):
- def is_feature_enabled():
- if platform.system() == "Darwin":
- try:
- output = subprocess.check_output(
- ["/usr/sbin/sysctl", feature], stderr=subprocess.DEVNULL
- ).decode("utf-8")
- # If 'feature: 1' was output, then this feature is available and
- # the test should not be skipped.
- if re.match(r"%s: 1\s*" % feature, output):
- return None
- else:
- return "%s is not supported on this system." % feature
- except subprocess.CalledProcessError:
- return "%s is not supported on this system." % feature
+def skipUnlessFeature(cpu_feature: CPUFeature):
+ def hasFeature(test_case):
+ if not test_case.isSupported(cpu_feature):
+ return f"Unsupported CPU feature: {cpu_feature}"
+ return None
- return skipTestIfFn(is_feature_enabled)
+ return skipTestIfFn(hasFeature)
def skipIfBuildType(types: list[str]):
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index b7077f8..8074922 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -48,6 +48,7 @@ import unittest
# LLDB modules
import lldb
from . import configuration
+from . import cpu_feature
from . import decorators
from . import lldbplatformutil
from . import lldbtest_config
@@ -1315,39 +1316,6 @@ class Base(unittest.TestCase):
return True
return False
- def getCPUInfo(self):
- triple = self.dbg.GetSelectedPlatform().GetTriple()
-
- # TODO other platforms, please implement this function
- if not re.match(".*-.*-linux", triple):
- return ""
-
- # Need to do something different for non-Linux/Android targets
- cpuinfo_path = self.getBuildArtifact("cpuinfo")
- if configuration.lldb_platform_name:
- self.runCmd(
- 'platform get-file "/proc/cpuinfo" ' + cpuinfo_path, check=False
- )
- if not self.res.Succeeded():
- if self.TraceOn():
- print(
- 'Failed to get /proc/cpuinfo from remote: "{}"'.format(
- self.res.GetOutput().strip()
- )
- )
- print("All cpuinfo feature checks will fail.")
- return ""
- else:
- cpuinfo_path = "/proc/cpuinfo"
-
- try:
- with open(cpuinfo_path, "r") as f:
- cpuinfo = f.read()
- except:
- return ""
-
- return cpuinfo
-
def isAArch64(self):
"""Returns true if the architecture is AArch64."""
arch = self.getArchitecture().lower()
@@ -1360,39 +1328,47 @@ class Base(unittest.TestCase):
self.getArchitecture().lower().startswith("arm")
)
+ def isSupported(self, cpu_feature: cpu_feature.CPUFeature):
+ triple = self.dbg.GetSelectedPlatform().GetTriple()
+ cmd_runner = self.run_platform_command
+ return cpu_feature.is_supported(triple, cmd_runner)
+
def isAArch64SVE(self):
- return self.isAArch64() and "sve" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SVE)
def isAArch64SME(self):
- return self.isAArch64() and "sme" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SME)
def isAArch64SME2(self):
# If you have sme2, you also have sme.
- return self.isAArch64() and "sme2" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.SME2)
def isAArch64SMEFA64(self):
# smefa64 allows the use of the full A64 instruction set in streaming
# mode. This is required by certain test programs to setup register
# state.
- cpuinfo = self.getCPUInfo()
- return self.isAArch64() and "sme" in cpuinfo and "smefa64" in cpuinfo
+ return (
+ self.isAArch64()
+ and self.isSupported(cpu_feature.AArch64.SME)
+ and self.isSupported(cpu_feature.AArch64.SME_FA64)
+ )
def isAArch64MTE(self):
- return self.isAArch64() and "mte" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.MTE)
def isAArch64MTEStoreOnly(self):
- return self.isAArch64() and "mtestoreonly" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.MTE_STORE_ONLY)
def isAArch64GCS(self):
- return self.isAArch64() and "gcs" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.GCS)
def isAArch64PAuth(self):
if self.getArchitecture() == "arm64e":
return True
- return self.isAArch64() and "paca" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.PTR_AUTH)
def isAArch64FPMR(self):
- return self.isAArch64() and "fpmr" in self.getCPUInfo()
+ return self.isAArch64() and self.isSupported(cpu_feature.AArch64.FPMR)
def isAArch64Windows(self):
"""Returns true if the architecture is AArch64 and platform windows."""
@@ -1407,10 +1383,10 @@ class Base(unittest.TestCase):
return arch in ["loongarch64", "loongarch32"]
def isLoongArchLSX(self):
- return self.isLoongArch() and "lsx" in self.getCPUInfo()
+ return self.isLoongArch() and self.isSupported(cpu_feature.Loong.LSX)
def isLoongArchLASX(self):
- return self.isLoongArch() and "lasx" in self.getCPUInfo()
+ return self.isLoongArch() and self.isSupported(cpu_feature.Loong.LASX)
def isRISCV(self):
"""Returns true if the architecture is RISCV64 or RISCV32."""