Files
RedBear-OS/local/recipes/dev/libclc/source/lldb/examples/python/disasm-stress-test.py
T
vasilito cb424d7448 build: static patch-sanity linter (shift-left the malformed-patch class)
verify-patch-sanity.py validates every active recipe .patch has internally-
consistent hunk line counts — catching the 'malformed patch at line N' failure
at commit/CI/preflight time instead of hours into a cook. This cycle hit that
class three times (qtwaylandscanner, sddm, xwayland), each only discovered when
cookbook tried to apply the patch.

Running it across the repo found 29 latent malformed patches (validated against
GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22').
They were harmless only because they sit in vendored recipes (baked, not re-
applied) — but would fail on any version-bump re-derivation. --fix recounts the
hunk headers (body untouched) and repaired all 29.

Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test
(test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats
(empty placeholders, bare-@@ git hunks).
2026-08-01 05:13:02 +03:00

241 lines
6.4 KiB
Python
Executable File

#!/usr/bin/env python
import argparse
import datetime
import re
import subprocess
import sys
import time
parser = argparse.ArgumentParser(
description="Run an exhaustive test of the LLDB disassembler for a specific architecture."
)
parser.add_argument(
"--arch",
required=True,
action="store",
help="The architecture whose disassembler is to be tested",
)
parser.add_argument(
"--bytes",
required=True,
action="store",
type=int,
help="The byte width of instructions for that architecture",
)
parser.add_argument(
"--random",
required=False,
action="store_true",
help="Enables non-sequential testing",
)
parser.add_argument(
"--start",
required=False,
action="store",
type=int,
help="The first instruction value to test",
)
parser.add_argument(
"--skip",
required=False,
action="store",
type=int,
help="The interval between instructions to test",
)
parser.add_argument(
"--log",
required=False,
action="store",
help="A log file to write the most recent instruction being tested",
)
parser.add_argument(
"--time",
required=False,
action="store_true",
help="Every 100,000 instructions, print an ETA to standard out",
)
parser.add_argument(
"--lldb",
required=False,
action="store",
help="The path to LLDB.framework, if LLDB should be overridden",
)
arguments = sys.argv[1:]
arg_ns = parser.parse_args(arguments)
def AddLLDBToSysPathOnMacOSX():
def GetLLDBFrameworkPath():
lldb_path = subprocess.check_output(["xcrun", "-find", "lldb"])
re_result = re.match("(.*)/Developer/usr/bin/lldb", lldb_path)
if re_result is None:
return None
xcode_contents_path = re_result.group(1)
return xcode_contents_path + "/SharedFrameworks/LLDB.framework"
lldb_framework_path = GetLLDBFrameworkPath()
if lldb_framework_path is None:
print("Couldn't find LLDB.framework")
sys.exit(-1)
sys.path.append(lldb_framework_path + "/Resources/Python")
if arg_ns.lldb is None:
AddLLDBToSysPathOnMacOSX()
else:
sys.path.append(arg_ns.lldb + "/Resources/Python")
import lldb
debugger = lldb.SBDebugger.Create()
if not debugger.IsValid():
print("Couldn't create an SBDebugger")
sys.exit(-1)
target = debugger.CreateTargetWithFileAndArch(None, arg_ns.arch)
if not target.IsValid():
print("Couldn't create an SBTarget for architecture " + arg_ns.arch)
sys.exit(-1)
def ResetLogFile(log_file):
if log_file != sys.stdout:
log_file.seek(0)
def PrintByteArray(log_file, byte_array):
for byte in byte_array:
print(hex(byte) + " ", end=" ", file=log_file)
print(file=log_file)
class SequentialInstructionProvider:
def __init__(self, byte_width, log_file, start=0, skip=1):
self.m_byte_width = byte_width
self.m_log_file = log_file
self.m_start = start
self.m_skip = skip
self.m_value = start
self.m_last = (1 << (byte_width * 8)) - 1
def PrintCurrentState(self, ret):
ResetLogFile(self.m_log_file)
print(self.m_value, file=self.m_log_file)
PrintByteArray(self.m_log_file, ret)
def GetNextInstruction(self):
if self.m_value > self.m_last:
return None
ret = bytearray(self.m_byte_width)
for i in range(self.m_byte_width):
ret[self.m_byte_width - (i + 1)] = (self.m_value >> (i * 8)) & 255
self.PrintCurrentState(ret)
self.m_value += self.m_skip
return ret
def GetNumInstructions(self):
return (self.m_last - self.m_start) / self.m_skip
def __iter__(self):
return self
def next(self):
ret = self.GetNextInstruction()
if ret is None:
raise StopIteration
return ret
class RandomInstructionProvider:
def __init__(self, byte_width, log_file):
self.m_byte_width = byte_width
self.m_log_file = log_file
self.m_random_file = open("/dev/random", "r")
def PrintCurrentState(self, ret):
ResetLogFile(self.m_log_file)
PrintByteArray(self.m_log_file, ret)
def GetNextInstruction(self):
ret = bytearray(self.m_byte_width)
for i in range(self.m_byte_width):
ret[i] = self.m_random_file.read(1)
self.PrintCurrentState(ret)
return ret
def __iter__(self):
return self
def next(self):
ret = self.GetNextInstruction()
if ret is None:
raise StopIteration
return ret
log_file = None
def GetProviderWithArguments(args):
global log_file
if args.log is not None:
log_file = open(args.log, "w")
else:
log_file = sys.stdout
instruction_provider = None
if args.random:
instruction_provider = RandomInstructionProvider(args.bytes, log_file)
else:
start = 0
skip = 1
if args.start is not None:
start = args.start
if args.skip is not None:
skip = args.skip
instruction_provider = SequentialInstructionProvider(
args.bytes, log_file, start, skip
)
return instruction_provider
instruction_provider = GetProviderWithArguments(arg_ns)
fake_address = lldb.SBAddress()
actually_time = arg_ns.time and not arg_ns.random
if actually_time:
num_instructions_logged = 0
total_num_instructions = instruction_provider.GetNumInstructions()
start_time = time.time()
for inst_bytes in instruction_provider:
if actually_time:
if (num_instructions_logged != 0) and (num_instructions_logged % 100000 == 0):
curr_time = time.time()
elapsed_time = curr_time - start_time
remaining_time = float(total_num_instructions - num_instructions_logged) * (
float(elapsed_time) / float(num_instructions_logged)
)
print(str(datetime.timedelta(seconds=remaining_time)))
num_instructions_logged = num_instructions_logged + 1
inst_list = target.GetInstructions(fake_address, inst_bytes)
if not inst_list.IsValid():
print("Invalid instruction list", file=log_file)
continue
inst = inst_list.GetInstructionAtIndex(0)
if not inst.IsValid():
print("Invalid instruction", file=log_file)
continue
instr_output_stream = lldb.SBStream()
inst.GetDescription(instr_output_stream)
print(instr_output_stream.GetData(), file=log_file)