cb424d7448
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).
628 lines
20 KiB
Python
628 lines
20 KiB
Python
#!/usr/bin/env python
|
|
|
|
import lldb
|
|
import shlex
|
|
import sys
|
|
|
|
try:
|
|
from tkinter import *
|
|
import tkinter.ttk as ttk
|
|
except ImportError:
|
|
from Tkinter import *
|
|
import ttk
|
|
|
|
|
|
class ValueTreeItemDelegate(object):
|
|
def __init__(self, value):
|
|
self.value = value
|
|
|
|
def get_item_dictionary(self):
|
|
name = self.value.name
|
|
if name is None:
|
|
name = ""
|
|
typename = self.value.type
|
|
if typename is None:
|
|
typename = ""
|
|
value = self.value.value
|
|
if value is None:
|
|
value = ""
|
|
summary = self.value.summary
|
|
if summary is None:
|
|
summary = ""
|
|
has_children = self.value.MightHaveChildren()
|
|
return {
|
|
"#0": name,
|
|
"typename": typename,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": has_children,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
for i in range(self.value.num_children):
|
|
item_delegate = ValueTreeItemDelegate(self.value.GetChildAtIndex(i))
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class FrameTreeItemDelegate(object):
|
|
def __init__(self, frame):
|
|
self.frame = frame
|
|
|
|
def get_item_dictionary(self):
|
|
id = self.frame.GetFrameID()
|
|
name = "frame #%u" % (id)
|
|
value = "0x%16.16x" % (self.frame.GetPC())
|
|
stream = lldb.SBStream()
|
|
self.frame.GetDescription(stream)
|
|
summary = stream.GetData().split("`")[1]
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": self.frame.GetVariables(True, True, True, True).GetSize() > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
variables = self.frame.GetVariables(True, True, True, True)
|
|
n = variables.GetSize()
|
|
for i in range(n):
|
|
item_delegate = ValueTreeItemDelegate(variables[i])
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class ThreadTreeItemDelegate(object):
|
|
def __init__(self, thread):
|
|
self.thread = thread
|
|
|
|
def get_item_dictionary(self):
|
|
num_frames = self.thread.GetNumFrames()
|
|
name = "thread #%u" % (self.thread.GetIndexID())
|
|
value = "0x%x" % (self.thread.GetThreadID())
|
|
summary = "%u frames" % (num_frames)
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": num_frames > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
for frame in self.thread:
|
|
item_delegate = FrameTreeItemDelegate(frame)
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class ProcessTreeItemDelegate(object):
|
|
def __init__(self, process):
|
|
self.process = process
|
|
|
|
def get_item_dictionary(self):
|
|
id = self.process.GetProcessID()
|
|
num_threads = self.process.GetNumThreads()
|
|
value = str(self.process.GetProcessID())
|
|
summary = self.process.target.executable.fullpath
|
|
return {
|
|
"#0": "process",
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": num_threads > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
for thread in self.process:
|
|
item_delegate = ThreadTreeItemDelegate(thread)
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class TargetTreeItemDelegate(object):
|
|
def __init__(self, target):
|
|
self.target = target
|
|
|
|
def get_item_dictionary(self):
|
|
value = str(self.target.triple)
|
|
summary = self.target.executable.fullpath
|
|
return {
|
|
"#0": "target",
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": True,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
image_item_delegate = TargetImagesTreeItemDelegate(self.target)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class TargetImagesTreeItemDelegate(object):
|
|
def __init__(self, target):
|
|
self.target = target
|
|
|
|
def get_item_dictionary(self):
|
|
value = str(self.target.triple)
|
|
summary = self.target.executable.fullpath
|
|
num_modules = self.target.GetNumModules()
|
|
return {
|
|
"#0": "images",
|
|
"value": "",
|
|
"summary": "%u images" % num_modules,
|
|
"children": num_modules > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
for i in range(self.target.GetNumModules()):
|
|
module = self.target.GetModuleAtIndex(i)
|
|
image_item_delegate = ModuleTreeItemDelegate(self.target, module, i)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class ModuleTreeItemDelegate(object):
|
|
def __init__(self, target, module, index):
|
|
self.target = target
|
|
self.module = module
|
|
self.index = index
|
|
|
|
def get_item_dictionary(self):
|
|
name = "module %u" % (self.index)
|
|
value = self.module.file.basename
|
|
summary = self.module.file.dirname
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": True,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
sections_item_delegate = ModuleSectionsTreeItemDelegate(
|
|
self.target, self.module
|
|
)
|
|
item_dicts.append(sections_item_delegate.get_item_dictionary())
|
|
|
|
symbols_item_delegate = ModuleSymbolsTreeItemDelegate(self.target, self.module)
|
|
item_dicts.append(symbols_item_delegate.get_item_dictionary())
|
|
|
|
comp_units_item_delegate = ModuleCompileUnitsTreeItemDelegate(
|
|
self.target, self.module
|
|
)
|
|
item_dicts.append(comp_units_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class ModuleSectionsTreeItemDelegate(object):
|
|
def __init__(self, target, module):
|
|
self.target = target
|
|
self.module = module
|
|
|
|
def get_item_dictionary(self):
|
|
name = "sections"
|
|
value = ""
|
|
summary = "%u sections" % (self.module.GetNumSections())
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": True,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
num_sections = self.module.GetNumSections()
|
|
for i in range(num_sections):
|
|
section = self.module.GetSectionAtIndex(i)
|
|
image_item_delegate = SectionTreeItemDelegate(self.target, section)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class SectionTreeItemDelegate(object):
|
|
def __init__(self, target, section):
|
|
self.target = target
|
|
self.section = section
|
|
|
|
def get_item_dictionary(self):
|
|
name = self.section.name
|
|
section_load_addr = self.section.GetLoadAddress(self.target)
|
|
if section_load_addr != lldb.LLDB_INVALID_ADDRESS:
|
|
value = "0x%16.16x" % (section_load_addr)
|
|
else:
|
|
value = "0x%16.16x *" % (self.section.file_addr)
|
|
summary = ""
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": self.section.GetNumSubSections() > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
num_sections = self.section.GetNumSubSections()
|
|
for i in range(num_sections):
|
|
section = self.section.GetSubSectionAtIndex(i)
|
|
image_item_delegate = SectionTreeItemDelegate(self.target, section)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class ModuleCompileUnitsTreeItemDelegate(object):
|
|
def __init__(self, target, module):
|
|
self.target = target
|
|
self.module = module
|
|
|
|
def get_item_dictionary(self):
|
|
name = "compile units"
|
|
value = ""
|
|
summary = "%u compile units" % (self.module.GetNumSections())
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": self.module.GetNumCompileUnits() > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
num_cus = self.module.GetNumCompileUnits()
|
|
for i in range(num_cus):
|
|
cu = self.module.GetCompileUnitAtIndex(i)
|
|
image_item_delegate = CompileUnitTreeItemDelegate(self.target, cu)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class CompileUnitTreeItemDelegate(object):
|
|
def __init__(self, target, cu):
|
|
self.target = target
|
|
self.cu = cu
|
|
|
|
def get_item_dictionary(self):
|
|
name = self.cu.GetFileSpec().basename
|
|
value = ""
|
|
num_lines = self.cu.GetNumLineEntries()
|
|
summary = ""
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": num_lines > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
item_delegate = LineTableTreeItemDelegate(self.target, self.cu)
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class LineTableTreeItemDelegate(object):
|
|
def __init__(self, target, cu):
|
|
self.target = target
|
|
self.cu = cu
|
|
|
|
def get_item_dictionary(self):
|
|
name = "line table"
|
|
value = ""
|
|
num_lines = self.cu.GetNumLineEntries()
|
|
summary = "%u line entries" % (num_lines)
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": num_lines > 0,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
num_lines = self.cu.GetNumLineEntries()
|
|
for i in range(num_lines):
|
|
line_entry = self.cu.GetLineEntryAtIndex(i)
|
|
item_delegate = LineEntryTreeItemDelegate(self.target, line_entry, i)
|
|
item_dicts.append(item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class LineEntryTreeItemDelegate(object):
|
|
def __init__(self, target, line_entry, index):
|
|
self.target = target
|
|
self.line_entry = line_entry
|
|
self.index = index
|
|
|
|
def get_item_dictionary(self):
|
|
name = str(self.index)
|
|
address = self.line_entry.GetStartAddress()
|
|
load_addr = address.GetLoadAddress(self.target)
|
|
if load_addr != lldb.LLDB_INVALID_ADDRESS:
|
|
value = "0x%16.16x" % (load_addr)
|
|
else:
|
|
value = "0x%16.16x *" % (address.file_addr)
|
|
summary = (
|
|
self.line_entry.GetFileSpec().fullpath + ":" + str(self.line_entry.line)
|
|
)
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": False,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
return item_dicts
|
|
|
|
|
|
class InstructionTreeItemDelegate(object):
|
|
def __init__(self, target, instr):
|
|
self.target = target
|
|
self.instr = instr
|
|
|
|
def get_item_dictionary(self):
|
|
address = self.instr.GetAddress()
|
|
load_addr = address.GetLoadAddress(self.target)
|
|
if load_addr != lldb.LLDB_INVALID_ADDRESS:
|
|
name = "0x%16.16x" % (load_addr)
|
|
else:
|
|
name = "0x%16.16x *" % (address.file_addr)
|
|
value = (
|
|
self.instr.GetMnemonic(self.target)
|
|
+ " "
|
|
+ self.instr.GetOperands(self.target)
|
|
)
|
|
summary = self.instr.GetComment(self.target)
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": False,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
|
|
class ModuleSymbolsTreeItemDelegate(object):
|
|
def __init__(self, target, module):
|
|
self.target = target
|
|
self.module = module
|
|
|
|
def get_item_dictionary(self):
|
|
name = "symbols"
|
|
value = ""
|
|
summary = "%u symbols" % (self.module.GetNumSymbols())
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": True,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
num_symbols = self.module.GetNumSymbols()
|
|
for i in range(num_symbols):
|
|
symbol = self.module.GetSymbolAtIndex(i)
|
|
image_item_delegate = SymbolTreeItemDelegate(self.target, symbol, i)
|
|
item_dicts.append(image_item_delegate.get_item_dictionary())
|
|
return item_dicts
|
|
|
|
|
|
class SymbolTreeItemDelegate(object):
|
|
def __init__(self, target, symbol, index):
|
|
self.target = target
|
|
self.symbol = symbol
|
|
self.index = index
|
|
|
|
def get_item_dictionary(self):
|
|
address = self.symbol.GetStartAddress()
|
|
name = "[%u]" % self.index
|
|
symbol_load_addr = address.GetLoadAddress(self.target)
|
|
if symbol_load_addr != lldb.LLDB_INVALID_ADDRESS:
|
|
value = "0x%16.16x" % (symbol_load_addr)
|
|
else:
|
|
value = "0x%16.16x *" % (address.file_addr)
|
|
summary = self.symbol.name
|
|
return {
|
|
"#0": name,
|
|
"value": value,
|
|
"summary": summary,
|
|
"children": False,
|
|
"tree-item-delegate": self,
|
|
}
|
|
|
|
def get_child_item_dictionaries(self):
|
|
item_dicts = list()
|
|
return item_dicts
|
|
|
|
|
|
class DelegateTree(ttk.Frame):
|
|
def __init__(self, column_dicts, delegate, title, name):
|
|
ttk.Frame.__init__(self, name=name)
|
|
self.pack(expand=Y, fill=BOTH)
|
|
self.master.title(title)
|
|
self.delegate = delegate
|
|
self.columns_dicts = column_dicts
|
|
self.item_id_to_item_dict = dict()
|
|
frame = Frame(self)
|
|
frame.pack(side=TOP, fill=BOTH, expand=Y)
|
|
self._create_treeview(frame)
|
|
self._populate_root()
|
|
|
|
def _create_treeview(self, parent):
|
|
frame = ttk.Frame(parent)
|
|
frame.pack(side=TOP, fill=BOTH, expand=Y)
|
|
|
|
column_ids = list()
|
|
for i in range(1, len(self.columns_dicts)):
|
|
column_ids.append(self.columns_dicts[i]["id"])
|
|
# create the tree and scrollbars
|
|
self.tree = ttk.Treeview(columns=column_ids)
|
|
|
|
scroll_bar_v = ttk.Scrollbar(orient=VERTICAL, command=self.tree.yview)
|
|
scroll_bar_h = ttk.Scrollbar(orient=HORIZONTAL, command=self.tree.xview)
|
|
self.tree["yscroll"] = scroll_bar_v.set
|
|
self.tree["xscroll"] = scroll_bar_h.set
|
|
|
|
# setup column headings and columns properties
|
|
for columns_dict in self.columns_dicts:
|
|
self.tree.heading(
|
|
columns_dict["id"],
|
|
text=columns_dict["text"],
|
|
anchor=columns_dict["anchor"],
|
|
)
|
|
self.tree.column(columns_dict["id"], stretch=columns_dict["stretch"])
|
|
|
|
# add tree and scrollbars to frame
|
|
self.tree.grid(in_=frame, row=0, column=0, sticky=NSEW)
|
|
scroll_bar_v.grid(in_=frame, row=0, column=1, sticky=NS)
|
|
scroll_bar_h.grid(in_=frame, row=1, column=0, sticky=EW)
|
|
|
|
# set frame resizing priorities
|
|
frame.rowconfigure(0, weight=1)
|
|
frame.columnconfigure(0, weight=1)
|
|
|
|
# action to perform when a node is expanded
|
|
self.tree.bind("<<TreeviewOpen>>", self._update_tree)
|
|
|
|
def insert_items(self, parent_id, item_dicts):
|
|
for item_dict in item_dicts:
|
|
name = None
|
|
values = list()
|
|
first = True
|
|
for columns_dict in self.columns_dicts:
|
|
if first:
|
|
name = item_dict[columns_dict["id"]]
|
|
first = False
|
|
else:
|
|
values.append(item_dict[columns_dict["id"]])
|
|
item_id = self.tree.insert(
|
|
parent_id, END, text=name, values=values # root item has an empty name
|
|
)
|
|
self.item_id_to_item_dict[item_id] = item_dict
|
|
if item_dict["children"]:
|
|
self.tree.insert(item_id, END, text="dummy")
|
|
|
|
def _populate_root(self):
|
|
# use current directory as root node
|
|
self.insert_items("", self.delegate.get_child_item_dictionaries())
|
|
|
|
def _update_tree(self, event):
|
|
# user expanded a node - build the related directory
|
|
item_id = self.tree.focus() # the id of the expanded node
|
|
children = self.tree.get_children(item_id)
|
|
if len(children):
|
|
first_child = children[0]
|
|
# if the node only has a 'dummy' child, remove it and
|
|
# build new directory; skip if the node is already
|
|
# populated
|
|
if self.tree.item(first_child, option="text") == "dummy":
|
|
self.tree.delete(first_child)
|
|
item_dict = self.item_id_to_item_dict[item_id]
|
|
item_dicts = item_dict[
|
|
"tree-item-delegate"
|
|
].get_child_item_dictionaries()
|
|
self.insert_items(item_id, item_dicts)
|
|
|
|
|
|
@lldb.command("tk-variables")
|
|
def tk_variable_display(debugger, command, result, dict):
|
|
# needed for tree creation in TK library as it uses sys.argv...
|
|
sys.argv = ["tk-variables"]
|
|
target = debugger.GetSelectedTarget()
|
|
if not target:
|
|
print("invalid target", file=result)
|
|
return
|
|
process = target.GetProcess()
|
|
if not process:
|
|
print("invalid process", file=result)
|
|
return
|
|
thread = process.GetSelectedThread()
|
|
if not thread:
|
|
print("invalid thread", file=result)
|
|
return
|
|
frame = thread.GetSelectedFrame()
|
|
if not frame:
|
|
print("invalid frame", file=result)
|
|
return
|
|
# Parse command line args
|
|
command_args = shlex.split(command)
|
|
column_dicts = [
|
|
{"id": "#0", "text": "Name", "anchor": W, "stretch": 0},
|
|
{"id": "typename", "text": "Type", "anchor": W, "stretch": 0},
|
|
{"id": "value", "text": "Value", "anchor": W, "stretch": 0},
|
|
{"id": "summary", "text": "Summary", "anchor": W, "stretch": 1},
|
|
]
|
|
tree = DelegateTree(
|
|
column_dicts, FrameTreeItemDelegate(frame), "Variables", "lldb-tk-variables"
|
|
)
|
|
tree.mainloop()
|
|
|
|
|
|
@lldb.command("tk-process")
|
|
def tk_process_display(debugger, command, result, dict):
|
|
# needed for tree creation in TK library as it uses sys.argv...
|
|
sys.argv = ["tk-process"]
|
|
target = debugger.GetSelectedTarget()
|
|
if not target:
|
|
print("invalid target", file=result)
|
|
return
|
|
process = target.GetProcess()
|
|
if not process:
|
|
print("invalid process", file=result)
|
|
return
|
|
# Parse command line args
|
|
columnd_dicts = [
|
|
{"id": "#0", "text": "Name", "anchor": W, "stretch": 0},
|
|
{"id": "value", "text": "Value", "anchor": W, "stretch": 0},
|
|
{"id": "summary", "text": "Summary", "anchor": W, "stretch": 1},
|
|
]
|
|
command_args = shlex.split(command)
|
|
tree = DelegateTree(
|
|
columnd_dicts, ProcessTreeItemDelegate(process), "Process", "lldb-tk-process"
|
|
)
|
|
tree.mainloop()
|
|
|
|
|
|
@lldb.command("tk-target")
|
|
def tk_target_display(debugger, command, result, dict):
|
|
# needed for tree creation in TK library as it uses sys.argv...
|
|
sys.argv = ["tk-target"]
|
|
target = debugger.GetSelectedTarget()
|
|
if not target:
|
|
print("invalid target", file=result)
|
|
return
|
|
# Parse command line args
|
|
columnd_dicts = [
|
|
{"id": "#0", "text": "Name", "anchor": W, "stretch": 0},
|
|
{"id": "value", "text": "Value", "anchor": W, "stretch": 0},
|
|
{"id": "summary", "text": "Summary", "anchor": W, "stretch": 1},
|
|
]
|
|
command_args = shlex.split(command)
|
|
tree = DelegateTree(
|
|
columnd_dicts, TargetTreeItemDelegate(target), "Target", "lldb-tk-target"
|
|
)
|
|
tree.mainloop()
|