Compare commits

...

3 Commits

Author SHA1 Message Date
Julian Lettner
a1a4f3847c
[LLDB][test] Fix formatting 2025-08-15 19:48:10 -07:00
Julian Lettner
dd5f0b829f
[LLDB] Use new CPU feature check for @skipUnlessFeature decorator 2025-08-15 19:13:36 -07:00
Julian Lettner
fd3eff77a2
[LLDB] Add CPU feature check support to Darwin
Add new `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.Base.run_platform_command()` which uses
LLDB's `platform shell <cmd>` command.
2025-08-15 19:13:16 -07:00
5 changed files with 90 additions and 84 deletions

View File

@ -0,0 +1,58 @@
"""
Platform-agnostic helper to query for CPU features.
"""
import re
class CPUFeature:
def __init__(self, linux_cpu_info_flag: str, darwin_sysctl_key: str):
self.cpu_info_flag = linux_cpu_info_flag
self.sysctl_key = darwin_sysctl_key
def __str__(self):
return self.cpu_info_flag
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):
cmd = "cat /proc/cpuinfo"
err, retcode, output = cmd_runner(cmd)
if err.Fail() or retcode != 0:
err_msg = f"cat /proc/cpuinfo failed: {output}"
return err_msg, False
return None, (self.cpu_info_flag in output)
def _is_supported_darwin(self, cmd_runner):
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")
# List of CPU features
FPMR = CPUFeature("fpmr", "???")
GCS = CPUFeature("gcs", "???")
LASX = CPUFeature("lasx", "???")
LSX = CPUFeature("lsx", "???")
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", "???")

View File

@ -23,6 +23,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:
@ -1127,24 +1128,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]):

View File

@ -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.SVE)
def isAArch64SME(self):
return self.isAArch64() and "sme" in self.getCPUInfo()
return self.isAArch64() and self.isSupported(cpu_feature.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.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.SME)
and self.isSupported(cpu_feature.SME_FA64)
)
def isAArch64MTE(self):
return self.isAArch64() and "mte" in self.getCPUInfo()
return self.isAArch64() and self.isSupported(cpu_feature.MTE)
def isAArch64MTEStoreOnly(self):
return self.isAArch64() and "mtestoreonly" in self.getCPUInfo()
return self.isAArch64() and self.isSupported(cpu_feature.MTE_STORE_ONLY)
def isAArch64GCS(self):
return self.isAArch64() and "gcs" in self.getCPUInfo()
return self.isAArch64() and self.isSupported(cpu_feature.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.PTR_AUTH)
def isAArch64FPMR(self):
return self.isAArch64() and "fpmr" in self.getCPUInfo()
return self.isAArch64() and self.isSupported(cpu_feature.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.LSX)
def isLoongArchLASX(self):
return self.isLoongArch() and "lasx" in self.getCPUInfo()
return self.isLoongArch() and self.isSupported(cpu_feature.LASX)
def isRISCV(self):
"""Returns true if the architecture is RISCV64 or RISCV32."""

View File

@ -21,24 +21,6 @@ class RegisterCommandsTestCase(TestBase):
self.dbg.GetSelectedTarget().GetProcess().Destroy()
TestBase.tearDown(self)
# on macOS, detect if the current machine is arm64 and supports SME
def get_sme_available(self):
if self.getArchitecture() != "arm64":
return None
try:
sysctl_output = subprocess.check_output(
["sysctl", "hw.optional.arm.FEAT_SME"]
).decode("utf-8")
except subprocess.CalledProcessError:
return None
m = re.match(r"hw\.optional\.arm\.FEAT_SME: (\w+)", sysctl_output)
if m:
if int(m.group(1)) == 1:
return True
else:
return False
return None
@skipIfiOSSimulator
@skipIf(archs=no_match(["amd64", "arm$", "i386", "x86_64"]))
@expectedFailureAll(oslist=["freebsd", "netbsd"], bugnumber="llvm.org/pr48371")
@ -51,7 +33,7 @@ class RegisterCommandsTestCase(TestBase):
self.log_enable("registers")
error_str_matched = False
if self.get_sme_available() and self.platformIsDarwin():
if self.isAArch64SME() and self.platformIsDarwin():
# On Darwin AArch64 SME machines, we will have unavailable
# registers when not in Streaming SVE Mode/SME, so
# `register read -a` will report that some registers

View File

@ -1,6 +1,7 @@
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.cpu_feature as cpu_feature
import lldbsuite.test.lldbutil as lldbutil
import os
@ -9,10 +10,9 @@ class TestSMERegistersDarwin(TestBase):
NO_DEBUG_INFO_TESTCASE = True
mydir = TestBase.compute_mydir(__file__)
@skipIfRemote
@skipUnlessDarwin
@skipUnlessFeature("hw.optional.arm.FEAT_SME")
@skipUnlessFeature("hw.optional.arm.FEAT_SME2")
@skipUnlessFeature(cpu_feature.SME)
@skipUnlessFeature(cpu_feature.SME2)
# thread_set_state/thread_get_state only avail in macOS 15.4+
@skipIf(macos_version=["<", "15.4"])
def test(self):