lldb test: fix formatting
This commit is contained in:
@@ -9,9 +9,11 @@ import cmd
|
|||||||
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, line_buffering=False)
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, line_buffering=False)
|
||||||
|
|
||||||
|
|
||||||
def print_to_lldb(*args, **kwargs):
|
def print_to_lldb(*args, **kwargs):
|
||||||
print(*args, **kwargs, flush=True)
|
print(*args, **kwargs, flush=True)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Test:
|
class Test:
|
||||||
source_file: str
|
source_file: str
|
||||||
@@ -19,6 +21,7 @@ class Test:
|
|||||||
variable: str
|
variable: str
|
||||||
expected_value: str
|
expected_value: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TestResult:
|
class TestResult:
|
||||||
test: Test
|
test: Test
|
||||||
@@ -28,6 +31,7 @@ class TestResult:
|
|||||||
missing: set = None
|
missing: set = None
|
||||||
extra: set = None
|
extra: set = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TestCase:
|
class TestCase:
|
||||||
source_file: str
|
source_file: str
|
||||||
@@ -35,12 +39,14 @@ class TestCase:
|
|||||||
end_line: int
|
end_line: int
|
||||||
tests: List[Test]
|
tests: List[Test]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CaseResult:
|
class CaseResult:
|
||||||
test_case: TestCase
|
test_case: TestCase
|
||||||
function: str
|
function: str
|
||||||
results: List[TestResult]
|
results: List[TestResult]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TestResults:
|
class TestResults:
|
||||||
total: int = 0
|
total: int = 0
|
||||||
@@ -48,6 +54,7 @@ class TestResults:
|
|||||||
failed: int = 0
|
failed: int = 0
|
||||||
case_results: List[CaseResult] = field(default_factory=list)
|
case_results: List[CaseResult] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
class LLDBDebugger:
|
class LLDBDebugger:
|
||||||
def __init__(self, executable_path, plugin_path=None):
|
def __init__(self, executable_path, plugin_path=None):
|
||||||
self.executable_path = executable_path
|
self.executable_path = executable_path
|
||||||
@@ -56,19 +63,28 @@ class LLDBDebugger:
|
|||||||
self.debugger.SetAsync(False)
|
self.debugger.SetAsync(False)
|
||||||
self.target = None
|
self.target = None
|
||||||
self.process = None
|
self.process = None
|
||||||
|
self.type_mapping = {
|
||||||
|
'long': 'int',
|
||||||
|
'unsigned long': 'uint',
|
||||||
|
# Add more mappings as needed
|
||||||
|
}
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
if self.plugin_path:
|
if self.plugin_path:
|
||||||
self.debugger.HandleCommand(f'command script import "{self.plugin_path}"')
|
self.debugger.HandleCommand(
|
||||||
|
f'command script import "{self.plugin_path}"')
|
||||||
|
|
||||||
self.target = self.debugger.CreateTarget(self.executable_path)
|
self.target = self.debugger.CreateTarget(self.executable_path)
|
||||||
if not self.target:
|
if not self.target:
|
||||||
raise Exception(f"Failed to create target for {self.executable_path}")
|
raise Exception(f"Failed to create target for {
|
||||||
|
self.executable_path}")
|
||||||
|
|
||||||
def set_breakpoint(self, file_spec, line_number):
|
def set_breakpoint(self, file_spec, line_number):
|
||||||
breakpoint = self.target.BreakpointCreateByLocation(file_spec, line_number)
|
breakpoint = self.target.BreakpointCreateByLocation(
|
||||||
|
file_spec, line_number)
|
||||||
if not breakpoint.IsValid():
|
if not breakpoint.IsValid():
|
||||||
raise Exception(f"Failed to set breakpoint at {file_spec}:{line_number}")
|
raise Exception(f"Failed to set breakpoint at {
|
||||||
|
file_spec}:{line_number}")
|
||||||
return breakpoint
|
return breakpoint
|
||||||
|
|
||||||
def run_to_breakpoint(self):
|
def run_to_breakpoint(self):
|
||||||
@@ -82,21 +98,128 @@ class LLDBDebugger:
|
|||||||
|
|
||||||
def get_variable_value(self, var_name):
|
def get_variable_value(self, var_name):
|
||||||
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
|
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
|
||||||
var = frame.FindVariable(var_name)
|
|
||||||
|
if isinstance(var_name, lldb.SBValue):
|
||||||
|
var = var_name
|
||||||
|
else:
|
||||||
|
actual_var_name = var_name.split('=')[0].strip()
|
||||||
|
if '(' in actual_var_name:
|
||||||
|
actual_var_name = actual_var_name.split('(')[-1].strip()
|
||||||
|
var = frame.FindVariable(actual_var_name)
|
||||||
|
|
||||||
|
return self.format_value(var)
|
||||||
|
|
||||||
|
def format_value(self, var):
|
||||||
if var.IsValid():
|
if var.IsValid():
|
||||||
type_name = var.GetTypeName()
|
type_name = var.GetTypeName()
|
||||||
if var.GetNumChildren() > 0:
|
var_type = var.GetType()
|
||||||
children = []
|
type_class = var_type.GetTypeClass()
|
||||||
for i in range(var.GetNumChildren()):
|
|
||||||
child = var.GetChildAtIndex(i)
|
if type_name.startswith('[]'): # Slice
|
||||||
child_name = child.GetName()
|
return self.format_slice(var)
|
||||||
child_value = child.GetValue()
|
elif var_type.IsArrayType():
|
||||||
children.append(f"{child_name} = {child_value}")
|
if type_class in [lldb.eTypeClassStruct, lldb.eTypeClassClass]:
|
||||||
return f"{type_name}({', '.join(children)})"
|
return self.format_custom_array(var)
|
||||||
|
else:
|
||||||
|
return self.format_array(var)
|
||||||
|
elif type_name == 'string': # String
|
||||||
|
return self.format_string(var)
|
||||||
|
elif type_name in ['complex64', 'complex128']:
|
||||||
|
return self.format_complex(var)
|
||||||
|
elif type_class in [lldb.eTypeClassStruct, lldb.eTypeClassClass]:
|
||||||
|
return self.format_struct(var)
|
||||||
else:
|
else:
|
||||||
value = var.GetValue() or var.GetSummary() or str(var)
|
value = var.GetValue()
|
||||||
return str(value) if value is not None else "None"
|
summary = var.GetSummary()
|
||||||
return None
|
if value is not None:
|
||||||
|
return str(value)
|
||||||
|
elif summary is not None:
|
||||||
|
return summary
|
||||||
|
else:
|
||||||
|
return "None"
|
||||||
|
return "None"
|
||||||
|
|
||||||
|
def format_slice(self, var):
|
||||||
|
length = int(var.GetChildMemberWithName('len').GetValue())
|
||||||
|
data_ptr = var.GetChildMemberWithName('data')
|
||||||
|
elements = []
|
||||||
|
|
||||||
|
# Get the actual pointer value
|
||||||
|
ptr_value = int(data_ptr.GetValue(), 16)
|
||||||
|
element_type = data_ptr.GetType().GetPointeeType()
|
||||||
|
element_size = element_type.GetByteSize()
|
||||||
|
|
||||||
|
for i in range(length):
|
||||||
|
element_address = ptr_value + i * element_size
|
||||||
|
element = self.target.CreateValueFromAddress(
|
||||||
|
f"element_{i}", lldb.SBAddress(element_address, self.target), element_type)
|
||||||
|
value = self.format_value(element)
|
||||||
|
elements.append(value)
|
||||||
|
|
||||||
|
type_name = var.GetType().GetName().split(
|
||||||
|
'[]')[-1] # Extract element type from slice type
|
||||||
|
type_name = self.type_mapping.get(type_name, type_name) # Use mapping
|
||||||
|
result = f"[]{type_name}[{', '.join(elements)}]"
|
||||||
|
return result
|
||||||
|
|
||||||
|
def format_array(self, var):
|
||||||
|
elements = []
|
||||||
|
for i in range(var.GetNumChildren()):
|
||||||
|
value = self.format_value(var.GetChildAtIndex(i))
|
||||||
|
elements.append(value)
|
||||||
|
array_size = var.GetNumChildren()
|
||||||
|
type_name = var.GetType().GetArrayElementType().GetName()
|
||||||
|
type_name = self.type_mapping.get(type_name, type_name) # Use mapping
|
||||||
|
return f"[{array_size}]{type_name}[{', '.join(elements)}]"
|
||||||
|
|
||||||
|
def format_custom_array(self, var):
|
||||||
|
elements = []
|
||||||
|
for i in range(var.GetNumChildren()):
|
||||||
|
element = var.GetChildAtIndex(i)
|
||||||
|
formatted = self.format_struct(element, include_type=False)
|
||||||
|
elements.append(formatted)
|
||||||
|
array_size = var.GetNumChildren()
|
||||||
|
type_name = var.GetType().GetArrayElementType().GetName()
|
||||||
|
return f"[{array_size}]{type_name}[{', '.join(elements)}]"
|
||||||
|
|
||||||
|
def format_pointer(self, var):
|
||||||
|
target = var.Dereference()
|
||||||
|
if target.IsValid():
|
||||||
|
return f"*{self.get_variable_value(target.GetName())}"
|
||||||
|
else:
|
||||||
|
return str(var.GetValue())
|
||||||
|
|
||||||
|
def format_string(self, var):
|
||||||
|
summary = var.GetSummary()
|
||||||
|
if summary is not None:
|
||||||
|
return summary.strip('"')
|
||||||
|
else:
|
||||||
|
data = var.GetChildMemberWithName('data').GetValue()
|
||||||
|
length = int(var.GetChildMemberWithName('len').GetValue())
|
||||||
|
if data and length:
|
||||||
|
error = lldb.SBError()
|
||||||
|
return self.process.ReadCStringFromMemory(int(data, 16), length + 1, error)
|
||||||
|
return "None"
|
||||||
|
|
||||||
|
def format_struct(self, var, include_type=True):
|
||||||
|
children = []
|
||||||
|
for i in range(var.GetNumChildren()):
|
||||||
|
child = var.GetChildAtIndex(i)
|
||||||
|
child_name = child.GetName()
|
||||||
|
child_value = self.format_value(child)
|
||||||
|
children.append(f"{child_name} = {child_value}")
|
||||||
|
|
||||||
|
struct_content = f"({', '.join(children)})"
|
||||||
|
if include_type:
|
||||||
|
struct_name = var.GetTypeName()
|
||||||
|
return f"{struct_name}{struct_content}"
|
||||||
|
else:
|
||||||
|
return struct_content
|
||||||
|
|
||||||
|
def format_complex(self, var):
|
||||||
|
real = var.GetChildMemberWithName('real').GetValue()
|
||||||
|
imag = var.GetChildMemberWithName('imag').GetValue()
|
||||||
|
return f"{var.GetTypeName()}(real = {real}, imag = {imag})"
|
||||||
|
|
||||||
def get_all_variable_names(self):
|
def get_all_variable_names(self):
|
||||||
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
|
frame = self.process.GetSelectedThread().GetFrameAtIndex(0)
|
||||||
@@ -112,7 +235,8 @@ class LLDBDebugger:
|
|||||||
lldb.SBDebugger.Destroy(self.debugger)
|
lldb.SBDebugger.Destroy(self.debugger)
|
||||||
|
|
||||||
def run_console(self):
|
def run_console(self):
|
||||||
print_to_lldb("\nEntering LLDB interactive mode. Type 'quit' to exit and continue with the next test case.")
|
print_to_lldb(
|
||||||
|
"\nEntering LLDB interactive mode. Type 'quit' to exit and continue with the next test case.")
|
||||||
|
|
||||||
old_stdin, old_stdout, old_stderr = sys.stdin, sys.stdout, sys.stderr
|
old_stdin, old_stdout, old_stderr = sys.stdin, sys.stdout, sys.stderr
|
||||||
sys.stdin, sys.stdout, sys.stderr = sys.__stdin__, sys.__stdout__, sys.__stderr__
|
sys.stdin, sys.stdout, sys.stderr = sys.__stdin__, sys.__stdout__, sys.__stderr__
|
||||||
@@ -211,13 +335,15 @@ def execute_tests(debugger, test_cases, interactive):
|
|||||||
results.case_results.append(case_result)
|
results.case_results.append(case_result)
|
||||||
|
|
||||||
# Print the current case test results before entering interactive mode
|
# Print the current case test results before entering interactive mode
|
||||||
print_to_lldb(f"\nTest case: {case_result.test_case.source_file}:{case_result.test_case.start_line}-{case_result.test_case.end_line} in function '{case_result.function}'")
|
print_to_lldb(f"\nTest case: {case_result.test_case.source_file}:{
|
||||||
|
case_result.test_case.start_line}-{case_result.test_case.end_line} in function '{case_result.function}'")
|
||||||
for result in case_result.results:
|
for result in case_result.results:
|
||||||
# Always print verbose results here
|
# Always print verbose results here
|
||||||
print_test_result(result, True)
|
print_test_result(result, True)
|
||||||
|
|
||||||
if interactive and any(r.status != 'pass' for r in case_result.results):
|
if interactive and any(r.status != 'pass' for r in case_result.results):
|
||||||
print_to_lldb("\nTest case failed. Entering LLDB interactive mode.")
|
print_to_lldb(
|
||||||
|
"\nTest case failed. Entering LLDB interactive mode.")
|
||||||
debugger.run_console()
|
debugger.run_console()
|
||||||
# After exiting the console, we need to ensure the process is in a valid state
|
# After exiting the console, we need to ensure the process is in a valid state
|
||||||
if debugger.process.GetState() == lldb.eStateRunning:
|
if debugger.process.GetState() == lldb.eStateRunning:
|
||||||
@@ -294,7 +420,8 @@ def execute_single_variable_test(debugger, test):
|
|||||||
|
|
||||||
def print_test_results(results: TestResults, verbose):
|
def print_test_results(results: TestResults, verbose):
|
||||||
for case_result in results.case_results:
|
for case_result in results.case_results:
|
||||||
print_to_lldb(f"\nTest case: {case_result.test_case.source_file}:{case_result.test_case.start_line}-{case_result.test_case.end_line} in function '{case_result.function}'")
|
print_to_lldb(f"\nTest case: {case_result.test_case.source_file}:{
|
||||||
|
case_result.test_case.start_line}-{case_result.test_case.end_line} in function '{case_result.function}'")
|
||||||
for result in case_result.results:
|
for result in case_result.results:
|
||||||
print_test_result(result, verbose)
|
print_test_result(result, verbose)
|
||||||
|
|
||||||
@@ -317,7 +444,8 @@ def print_test_result(result: TestResult, verbose):
|
|||||||
print_to_lldb(
|
print_to_lldb(
|
||||||
f"{status_symbol} Line {result.test.line_number}, {result.test.variable}: {status_text}")
|
f"{status_symbol} Line {result.test.line_number}, {result.test.variable}: {status_text}")
|
||||||
if result.test.variable == 'all variables':
|
if result.test.variable == 'all variables':
|
||||||
print_to_lldb(f" Variables: {', '.join(sorted(result.actual))}")
|
print_to_lldb(f" Variables: {
|
||||||
|
', '.join(sorted(result.actual))}")
|
||||||
else: # fail or error
|
else: # fail or error
|
||||||
print_to_lldb(
|
print_to_lldb(
|
||||||
f"{status_symbol} Line {result.test.line_number}, {result.test.variable}: {status_text}")
|
f"{status_symbol} Line {result.test.line_number}, {result.test.variable}: {status_text}")
|
||||||
@@ -337,27 +465,36 @@ def print_test_result(result: TestResult, verbose):
|
|||||||
print_to_lldb(f" Expected: {result.test.expected_value}")
|
print_to_lldb(f" Expected: {result.test.expected_value}")
|
||||||
print_to_lldb(f" Actual: {result.actual}")
|
print_to_lldb(f" Actual: {result.actual}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print_to_lldb(sys.argv)
|
print_to_lldb(sys.argv)
|
||||||
parser = argparse.ArgumentParser(description="LLDB 18 Debug Script with DWARF 5 Support")
|
parser = argparse.ArgumentParser(
|
||||||
|
description="LLDB 18 Debug Script with DWARF 5 Support")
|
||||||
parser.add_argument("executable", help="Path to the executable")
|
parser.add_argument("executable", help="Path to the executable")
|
||||||
parser.add_argument("sources", nargs='+', help="Paths to the source files")
|
parser.add_argument("sources", nargs='+', help="Paths to the source files")
|
||||||
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
|
parser.add_argument("-v", "--verbose", action="store_true",
|
||||||
parser.add_argument("-i", "--interactive", action="store_true", help="Enable interactive mode on test failure")
|
help="Enable verbose output")
|
||||||
|
parser.add_argument("-i", "--interactive", action="store_true",
|
||||||
|
help="Enable interactive mode on test failure")
|
||||||
parser.add_argument("--plugin", help="Path to the LLDB plugin")
|
parser.add_argument("--plugin", help="Path to the LLDB plugin")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
plugin_path = args.plugin or os.path.join(os.path.dirname(os.path.realpath(__file__)), "go_lldb_plugin.py")
|
plugin_path = args.plugin or os.path.join(os.path.dirname(
|
||||||
run_tests(args.executable, args.sources, args.verbose, args.interactive, plugin_path)
|
os.path.realpath(__file__)), "go_lldb_plugin.py")
|
||||||
|
run_tests(args.executable, args.sources,
|
||||||
|
args.verbose, args.interactive, plugin_path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|
||||||
def run_commands(debugger, command, result, internal_dict):
|
def run_commands(debugger, command, result, internal_dict):
|
||||||
print_to_lldb(sys.argv)
|
print_to_lldb(sys.argv)
|
||||||
main()
|
main()
|
||||||
debugger.HandleCommand("quit")
|
debugger.HandleCommand("quit")
|
||||||
|
|
||||||
|
|
||||||
def __lldb_init_module(debugger, internal_dict):
|
def __lldb_init_module(debugger, internal_dict):
|
||||||
# debugger.HandleCommand('command script add -f main.run_commands run_tests')
|
# debugger.HandleCommand('command script add -f main.run_commands run_tests')
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -118,10 +118,10 @@ func FuncWithAllTypeParams(
|
|||||||
// c64: complex64(real = 13, imag = 14)
|
// c64: complex64(real = 13, imag = 14)
|
||||||
// c128: complex128(real = 15, imag = 16)
|
// c128: complex128(real = 15, imag = 16)
|
||||||
// slice: []int[21, 22, 23]
|
// slice: []int[21, 22, 23]
|
||||||
// arr: [3]int[24, 25, 26)
|
// arr: [3]int[24, 25, 26]
|
||||||
// arr2: [3]E[(i = 27), (i = 28), (i = 29)]
|
// arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E[github.com/goplus/llgo/cl/_testdata/debug.E(i = 27), github.com/goplus/llgo/cl/_testdata/debug.E(i = 28), github.com/goplus/llgo/cl/_testdata/debug.E(i = 29)]
|
||||||
// s: hello
|
// s: hello
|
||||||
// e: (i = 30)
|
// e: github.com/goplus/llgo/cl/_testdata/debug.E(i = 30)
|
||||||
return 1, errors.New("some error")
|
return 1, errors.New("some error")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user