Skip to content

Commit d365371

Browse files
committed
Make breakpoint.run test run against LLDB as well as GDB
We start introducing a debugger-independent scripting API for tests so they can work with LLDB as well as GDB.
1 parent ca5c4d9 commit d365371

File tree

5 files changed

+139
-62
lines changed

5 files changed

+139
-62
lines changed

src/test/breakpoint.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
from util import *
22

3-
send_gdb('b C')
4-
expect_gdb('Breakpoint 1')
5-
6-
send_gdb('c')
3+
breakpoint = breakpoint_at_function('C')
4+
cont()
75

86
expect_rr('calling C')
7+
expect_breakpoint_stop(breakpoint)
98

10-
expect_gdb('Breakpoint 1(.*) C')
11-
12-
send_gdb('bt')
13-
expect_gdb('#0[^C]+C[^#]+#1[^B]+B[^#]+#2[^A]+A[^#]+#3[^m]+main')
9+
backtrace()
10+
expect_debugger('#0[^C]+C[^#]+#1[^B]+B[^#]+#2[^A]+A[^#]+#3.+main')
1411

1512
ok()

src/test/breakpoint.run

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
source `dirname $0`/util.sh
2-
debug_test_gdb_only
2+
debug_test

src/test/util.py

+88-45
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import pexpect, re, signal, sys, time
22

3-
__all__ = [ 'expect_gdb', 'send_gdb','expect_rr', 'expect_list',
4-
'restart_replay', 'interrupt_gdb', 'ok',
5-
'failed', 'iterlines_both', 'last_match', 'get_exe_arch',
6-
'get_gdb_version', 'set_breakpoint', 'set_watchpoint' ]
3+
__all__ = [ 'expect_rr', 'expect_list', 'expect_debugger',
4+
'restart_replay', 'interrupt_gdb', 'expect_gdb', 'send_gdb',
5+
'ok', 'failed', 'iterlines_both', 'last_match', 'get_exe_arch',
6+
'get_gdb_version', 'breakpoint_at_function',
7+
'watchpoint_at_address', 'cont', 'backtrace',
8+
'expect_breakpoint_stop', 'expect_watchpoint_stop' ]
79

810
# Don't use python timeout. Use test-monitor timeout instead.
911
TIMEOUT_SEC = 10000
1012
# The debugger and rr are part of the same process tree, so they share
1113
# stdin/stdout.
1214
child = None
15+
debugger_type = 'GDB'
1316

1417
# Public API
15-
def expect_gdb(what):
18+
def expect_debugger(what):
1619
expect(child, what)
1720

1821
def expect_list(pats):
@@ -28,31 +31,35 @@ def failed(why, e=None):
2831
clean_up()
2932
sys.exit(1)
3033

31-
def interrupt_gdb():
34+
def interrupt_debugger():
3235
try:
3336
child.kill(signal.SIGINT)
3437
except Exception as e:
35-
failed('interrupting gdb', e)
38+
failed('interrupting debugger', e)
3639
expect_gdb('stopped.')
3740

38-
def set_breakpoint(args):
39-
send_gdb(f'b {args}')
40-
expect_gdb(r'Breakpoint ([0-9]+)[^0-9]')
41-
global child
42-
return child.match.group(1)
43-
44-
def set_watchpoint(args):
45-
send_gdb(f'watch {args}')
46-
expect_gdb(r'atchpoint ([0-9]+)[^0-9]')
47-
global child
48-
return child.match.group(1)
49-
5041
def iterlines_both():
5142
return child
5243

5344
def last_match():
5445
return child.match
5546

47+
def expect_gdb(what):
48+
assert debugger_type == 'GDB'
49+
expect_debugger(what)
50+
51+
def interrupt_gdb():
52+
assert debugger_type == 'GDB'
53+
interrupt_debugger()
54+
55+
def send_gdb(what):
56+
assert debugger_type == 'GDB'
57+
send(child, "%s\n"%what)
58+
59+
def send_lldb(what):
60+
assert debugger_type == 'LLDB'
61+
send(child, "%s\n"%what)
62+
5663
# Restarts and continues execution
5764
def restart_replay(event=0):
5865
if event:
@@ -68,14 +75,61 @@ def restart_replay(event=0):
6875
expect_rr('stopped')
6976
send_gdb('c')
7077

71-
def send_gdb(what):
72-
send(child, "%s\n"%what)
78+
def breakpoint_at_function(function):
79+
send_debugger(f'break {function}', f'breakpoint set --name {function}')
80+
expect_debugger(r'Breakpoint (\d+)')
81+
return int(last_match().group(1))
82+
83+
size_to_type = {1: 'char', 2:'short', 4:'int', 8:'long long'}
84+
85+
def watchpoint_at_address(address, size):
86+
send_debugger(f'watch -l *({size_to_type[size]}*){address}',
87+
f'watchpoint set expression -s {size} -- {address}')
88+
expect_debugger(r'atchpoint (\d+)')
89+
return int(last_match().group(1))
90+
91+
def cont():
92+
send_debugger('continue', 'continue')
93+
94+
def backtrace():
95+
send_debugger('bt', 'thread backtrace')
96+
97+
def expect_breakpoint_stop(number):
98+
if debugger_type == 'GDB':
99+
expect_debugger("Breakpoint %d"%number)
100+
else:
101+
expect_debugger("stop reason = breakpoint %d"%number)
102+
103+
def expect_watchpoint_stop(number):
104+
if debugger_type == 'GDB':
105+
expect_debugger("atchpoint %d"%number)
106+
else:
107+
expect_debugger("stop reason = watchpoint %d"%number)
108+
109+
def send_debugger(gdb_cmd, lldb_cmd):
110+
if debugger_type == 'GDB':
111+
send_gdb(gdb_cmd)
112+
else:
113+
send_lldb(lldb_cmd)
73114

74115
def ok():
75-
send_gdb('q')
76-
send_gdb('y')
116+
send_debugger('quit', 'quit')
117+
send_debugger('y', 'y')
77118
clean_up()
78119

120+
def get_exe_arch():
121+
send_gdb('show architecture')
122+
expect_gdb(r'The target architecture is set (automatically|to "auto") \(currently "?([0-9a-z:-]+)"?\)\.?')
123+
global child
124+
return child.match.group(2)
125+
126+
def get_gdb_version():
127+
'''Return the gdb version'''
128+
send_gdb('python print(gdb.VERSION)')
129+
expect_gdb(r'(\d+.\d+)')
130+
global child
131+
return float(child.match.group(1))
132+
79133
# Internal helpers
80134
def clean_up():
81135
global child
@@ -100,25 +154,6 @@ def expect(prog, what):
100154
except Exception as e:
101155
failed('expecting "%s"'% (what), e)
102156

103-
def get_exe_arch():
104-
send_gdb('show architecture')
105-
expect_gdb(r'The target architecture is set (automatically|to "auto") \(currently "?([0-9a-z:-]+)"?\)\.?')
106-
global child
107-
return child.match.group(2)
108-
109-
def get_rr_cmd():
110-
'''Return the command that should be used to invoke rr, as the tuple
111-
(executable, array-of-args)'''
112-
rrargs = sys.argv[1:]
113-
return (rrargs[0], rrargs[1:])
114-
115-
def get_gdb_version():
116-
'''Return the gdb version'''
117-
send_gdb('python print(gdb.VERSION)')
118-
expect_gdb(r'(\d+.\d+)')
119-
global child
120-
return float(child.match.group(1))
121-
122157
def send(prog, what):
123158
try:
124159
prog.send(what)
@@ -127,11 +162,19 @@ def send(prog, what):
127162

128163
def set_up():
129164
global child
165+
global debugger_type
166+
args = sys.argv[1:]
167+
log_file = 'gdb_rr.log'
168+
if args[0] == '--lldb':
169+
debugger_type = 'LLDB'
170+
args = args[1:] + ['-d', 'lldb', '-o', '--no-use-colors']
171+
log_file = 'lldb_rr.log'
130172
try:
131-
child = pexpect.spawn(*get_rr_cmd(), codec_errors='ignore', timeout=TIMEOUT_SEC, encoding='utf-8', logfile=open('gdb_rr.log', 'w'))
173+
child = pexpect.spawn(args[0], args[1:], codec_errors='ignore',
174+
timeout=TIMEOUT_SEC, encoding='utf-8', logfile=open(log_file, 'w'))
132175
child.delaybeforesend = 0
133-
expect_gdb(r'\(rr\)')
176+
expect_debugger(r'\(rr\)')
134177
except Exception as e:
135-
failed('initializing rr and gdb', e)
178+
failed('initializing rr and debugger', e)
136179

137180
set_up()

src/test/util.sh

+40-3
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,28 @@ function do_ps { psflags=$1
337337
$RR_EXE $GLOBAL_OPTIONS ps $psflags
338338
}
339339

340+
# debug_lldb_only <expect-script-name> [replay-args]
341+
#
342+
# Load the "expect" script to drive replay of the recording of |exe|.
343+
# Only LLDB is tested.
344+
function debug_lldb_only { expectscript=$1; replayargs=$2
345+
_RR_TRACE_DIR="$workdir" test-monitor $TIMEOUT debug.err \
346+
python3 $TESTDIR/$expectscript.py --lldb \
347+
$RR_EXE $GLOBAL_OPTIONS replay -o-S -o$TESTDIR/test_setup.lldb $replayargs
348+
if [[ $? == 0 ]]; then
349+
passed_msg lldb
350+
else
351+
failed "debug script failed (lldb)"
352+
echo "--------------------------------------------------"
353+
echo "lldb_rr.log:"
354+
cat lldb_rr.log
355+
echo "--------------------------------------------------"
356+
echo "debug.err:"
357+
cat debug.err
358+
echo "--------------------------------------------------"
359+
fi
360+
}
361+
340362
# debug_gdb_only <expect-script-name> [replay-args]
341363
#
342364
# Load the "expect" script to drive replay of the recording of |exe|.
@@ -346,9 +368,9 @@ function debug_gdb_only { expectscript=$1; replayargs=$2
346368
python3 $TESTDIR/$expectscript.py \
347369
$RR_EXE $GLOBAL_OPTIONS replay -o-n -o-ix -o$TESTDIR/test_setup.gdb $replayargs
348370
if [[ $? == 0 ]]; then
349-
passed
371+
passed_msg gdb
350372
else
351-
failed "debug script failed"
373+
failed "debug script failed (lldb)"
352374
echo "--------------------------------------------------"
353375
echo "gdb_rr.log:"
354376
cat gdb_rr.log
@@ -368,6 +390,10 @@ function passed {
368390
echo "Test '$TESTNAME' PASSED"
369391
}
370392

393+
function passed_msg { msg=$1
394+
echo "Test '$TESTNAME' PASSED ($msg)"
395+
}
396+
371397
function just_check_replay_err {
372398
if [[ $(cat replay.err) != "" ]]; then
373399
failed ": error during replay:"
@@ -471,11 +497,22 @@ function compare_test { token=$1; replayflags=$2;
471497
check $token
472498
}
473499

474-
# debug_test_gdb_only
500+
# debug_test
475501
#
476502
# Record the test name passed to |util.sh|, then replay the recording
477503
# using the "expect" script $test-name.py, which is responsible for
478504
# computing test pass/fail.
505+
function debug_test {
506+
record $TESTNAME
507+
debug_gdb_only $TEST_PREFIX$TESTNAME_NO_BITNESS
508+
debug_lldb_only $TEST_PREFIX$TESTNAME_NO_BITNESS
509+
}
510+
511+
# debug_test_gdb_only
512+
#
513+
# Record the test name passed to |util.sh|, then replay the recording
514+
# using the "expect" script $test-name.py, which is responsible for
515+
# computing test pass/fail. Only GDB is tested.
479516
function debug_test_gdb_only {
480517
record $TESTNAME
481518
debug_gdb_only $TEST_PREFIX$TESTNAME_NO_BITNESS

src/test/watchpoint_unaligned.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
from util import *
22

3-
bp = set_breakpoint('breakpoint')
3+
breakpoint = breakpoint_at_function('breakpoint')
44

55
def test():
6-
for type in ['uint16_t', 'uint32_t', 'uint64_t']:
6+
for size in [2, 4, 8]:
77
send_gdb('c')
8-
expect_gdb(f'Breakpoint {bp}')
8+
expect_breakpoint_stop(breakpoint)
99
# Get the value of `wp_addr` from the parent frame.
1010
# On Ubuntu 20 LTS, gdb stops before the `breakpoint` prelude so
1111
# gets the wrong value of `wp_addr`.
1212
send_gdb('up')
1313
expect_gdb('test')
14-
wp = set_watchpoint(f'-l *({type} *)wp_addr')
14+
wp = watchpoint_at_address('wp_addr', size)
1515
send_gdb('c')
16-
expect_gdb(f'watchpoint {wp}')
16+
expect_watchpoint_stop(wp)
1717
send_gdb(f'delete {wp}')
1818

1919
test()

0 commit comments

Comments
 (0)