mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
The initial version of the debugger testing transform instruments
assignments in a way that allows the debugger to sanity-check its
expression evaluator.
Given an assignment expression of the form:
```
a = b
```
The transform rewrites the relevant bits of the AST to look like this:
```
{ () -> () in
a = b
checkExpect("a", stringForPrintObject(a))
}()
```
The purpose of the rewrite is to make it easier to exercise the
debugger's expression evaluator in new contexts. This can be automated
by having the debugger set a breakpoint on checkExpect, running `expr
$Varname`, and comparing the result to the expected value generated by
the runtime.
While the initial version of this testing transform only supports
instrumenting assignments, it should be simple to teach it to do more
interesting rewrites.
There's a driver script available in SWIFT_BIN_DIR/lldb-check-expect to
simplfiy the process of launching and testing instrumented programs.
rdar://36032055
101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
'''
|
|
lldbCheckExpect.py
|
|
|
|
This is a driver script for testing lldb expression evaluation. It installs a
|
|
breakpoint command which executes tests inserted by the instrumentation pass,
|
|
and launches a target.
|
|
|
|
To use it, compile a target program with these flags:
|
|
|
|
-g -Xfrontend -debugger-testing-transform
|
|
|
|
Make sure the swift standard library is built with debug info. Then, launch
|
|
swift-lldb with "-o path/to/this/script -- <program> [<args>]". There is a
|
|
utility available in SWIFT_BINARY_DIR/bin/lldb-check-expect which automates
|
|
this, e.g:
|
|
|
|
./bin/lldb-check-expect <program>
|
|
...
|
|
Evaluating check-expect in closure #2 () -> () in CAPITest.swapTwoValues
|
|
Checked variable: a
|
|
Expected value : 98
|
|
Actual value : ...
|
|
'''
|
|
|
|
|
|
def unwrap(s):
|
|
'''
|
|
Strip non-essential character sequences from a string.
|
|
|
|
>>> unwrap('(String) value = "42\\\\n"')
|
|
'42'
|
|
>>> unwrap('(Int) $R0 = 42')
|
|
'42'
|
|
>>> unwrap('\\\\"foo\\"')
|
|
'foo'
|
|
>>> unwrap('foo\\nbar')
|
|
'foo\\nbar'
|
|
>>> unwrap(' foo ')
|
|
'foo'
|
|
'''
|
|
s = s[s.find('=') + 1:]
|
|
s = s.lstrip(' "')
|
|
s = s.rstrip('"')
|
|
if s.endswith('\\n'):
|
|
s = s[:-2]
|
|
if s.endswith('\\"'):
|
|
s = s[:-2]
|
|
if s.startswith('\\"'):
|
|
s = s[2:]
|
|
s = s.replace('\\n', '\n')
|
|
s = s.strip()
|
|
return s
|
|
|
|
|
|
def on_check_expect(frame, bp_loc, session):
|
|
parent_frame = frame.get_parent_frame()
|
|
parent_name = parent_frame.GetFunctionName()
|
|
print "Evaluating check-expect in", parent_name
|
|
|
|
# Note: If we fail to stringify the arguments in the check-expect frame,
|
|
# the standard library has probably not been compiled with debug info.
|
|
wrapped_var_name, wrapped_expected_value = map(str, frame.arguments)
|
|
|
|
var_name = unwrap(wrapped_var_name)
|
|
expected_value = unwrap(wrapped_expected_value)
|
|
|
|
# Evaluate the variable in the parent frame of the check-expect.
|
|
frame.thread.SetSelectedFrame(1)
|
|
expr_result = parent_frame.FindVariable(var_name).GetObjectDescription()
|
|
eval_result = unwrap(str(expr_result))
|
|
|
|
print " Checked variable:", var_name
|
|
print " Expected value :", expected_value
|
|
print " Actual value :", eval_result
|
|
|
|
if eval_result == expected_value:
|
|
# Do not stop execution.
|
|
return False
|
|
|
|
print "Found a possible expression evaluation failure."
|
|
|
|
for i, (c1, c2) in enumerate(zip(expected_value, eval_result)):
|
|
if c1 == c2:
|
|
continue
|
|
print " -> Character difference at index", i
|
|
print " -> Expected", c1, "but found", c2
|
|
break
|
|
else:
|
|
print " -> Expected string has length", len(expected_value)
|
|
print " -> Actual string has length", len(eval_result)
|
|
return True
|
|
|
|
|
|
def __lldb_init_module(debugger, internal_dict):
|
|
debugger.HandleCommand('breakpoint set -n _debuggerTestingCheckExpect '
|
|
'--breakpoint-name check_expect_bkpt')
|
|
debugger.HandleCommand('breakpoint command add --python-function '
|
|
'lldbCheckExpect.on_check_expect '
|
|
'--stop-on-error true check_expect_bkpt')
|
|
debugger.HandleCommand('run')
|