Skip to content

Commit

Permalink
Make breakpoint.run test run against LLDB as well as GDB
Browse files Browse the repository at this point in the history
  • Loading branch information
rocallahan committed Feb 17, 2024
1 parent df10f65 commit db8ae5f
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 45 deletions.
13 changes: 5 additions & 8 deletions src/test/breakpoint.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from util import *

send_gdb('b C')
expect_gdb('Breakpoint 1')

send_gdb('c')
breakpoint_at('C', 1)
cont()

expect_rr('calling C')
expect_breakpoint_stop(1)

expect_gdb('Breakpoint 1(.*) C')

send_gdb('bt')
expect_gdb('#0[^C]+C[^#]+#1[^B]+B[^#]+#2[^A]+A[^#]+#3[^m]+main')
backtrace()
expect_debugger('#0[^C]+C[^#]+#1[^B]+B[^#]+#2[^A]+A[^#]+#3.+main')

ok()
2 changes: 1 addition & 1 deletion src/test/breakpoint.run
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
source `dirname $0`/util.sh
debug_test_gdb_only
debug_test
106 changes: 73 additions & 33 deletions src/test/util.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import pexpect, re, signal, sys, time

__all__ = [ 'expect_gdb', 'send_gdb','expect_rr', 'expect_list',
'restart_replay', 'interrupt_gdb', 'ok',
'failed', 'iterlines_both', 'last_match', 'get_exe_arch',
'get_gdb_version' ]
__all__ = [ 'expect_rr', 'expect_list', 'expect_debugger',
'restart_replay', 'interrupt_gdb', 'expect_gdb', 'send_gdb',
'ok', 'failed', 'iterlines_both', 'last_match', 'get_exe_arch',
'get_gdb_version',
'breakpoint_at', 'cont', 'backtrace',
'expect_breakpoint_stop' ]

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

# Public API
def expect_gdb(what):
def expect_debugger(what):
expect(child, what)

def expect_list(pats):
Expand All @@ -28,11 +31,11 @@ def failed(why, e=None):
clean_up()
sys.exit(1)

def interrupt_gdb():
def interrupt_debugger():
try:
child.kill(signal.SIGINT)
except Exception as e:
failed('interrupting gdb', e)
failed('interrupting debugger', e)
expect_gdb('stopped.')

def iterlines_both():
Expand All @@ -41,6 +44,22 @@ def iterlines_both():
def last_match():
return child.match

def expect_gdb(what):
assert debugger_type == 'GDB'
expect_debugger(what)

def interrupt_gdb():
assert debugger_type == 'GDB'
interrupt_debugger()

def send_gdb(what):
assert debugger_type == 'GDB'
send(child, "%s\n"%what)

def send_lldb(what):
assert debugger_type == 'LLDB'
send(child, "%s\n"%what)

# Restarts and continues execution
def restart_replay(event=0):
if event:
Expand All @@ -56,14 +75,46 @@ def restart_replay(event=0):
expect_rr('stopped')
send_gdb('c')

def send_gdb(what):
send(child, "%s\n"%what)
def breakpoint_at(location, expected_number):
send_debugger('b %s'%location, 'b %s'%location)
expect_debugger('Breakpoint %d' % expected_number)

def cont():
send_debugger('continue', 'continue')

def backtrace():
send_debugger('bt', 'thread backtrace')

def expect_breakpoint_stop(number):
if debugger_type == 'GDB':
expect_debugger("Breakpoint %d"%number)
else:
expect_debugger("stop reason = breakpoint %d"%number)

def send_debugger(gdb_cmd, lldb_cmd):
if debugger_type == 'GDB':
send_gdb(gdb_cmd)
else:
send_lldb(lldb_cmd)

def ok():
send_gdb('q')
send_gdb('y')
send_debugger('quit', 'quit')
send_debugger('y', 'y')
clean_up()

def get_exe_arch():
send_gdb('show architecture')
expect_gdb(r'The target architecture is set (automatically|to "auto") \(currently "?([0-9a-z:-]+)"?\)\.?')
global child
return child.match.group(2)

def get_gdb_version():
'''Return the gdb version'''
send_gdb('python print(gdb.VERSION)')
expect_gdb(r'(\d+.\d+)')
global child
return float(child.match.group(1))

# Internal helpers
def clean_up():
global child
Expand All @@ -88,25 +139,6 @@ def expect(prog, what):
except Exception as e:
failed('expecting "%s"'% (what), e)

def get_exe_arch():
send_gdb('show architecture')
expect_gdb(r'The target architecture is set (automatically|to "auto") \(currently "?([0-9a-z:-]+)"?\)\.?')
global child
return child.match.group(2)

def get_rr_cmd():
'''Return the command that should be used to invoke rr, as the tuple
(executable, array-of-args)'''
rrargs = sys.argv[1:]
return (rrargs[0], rrargs[1:])

def get_gdb_version():
'''Return the gdb version'''
send_gdb('python print(gdb.VERSION)')
expect_gdb(r'(\d+.\d+)')
global child
return float(child.match.group(1))

def send(prog, what):
try:
prog.send(what)
Expand All @@ -115,11 +147,19 @@ def send(prog, what):

def set_up():
global child
global debugger_type
args = sys.argv[1:]
log_file = 'gdb_rr.log'
if args[0] == '--lldb':
debugger_type = 'LLDB'
args = args[1:] + ['-d', 'lldb', '-o', '--no-use-colors']
log_file = 'lldb_rr.log'
try:
child = pexpect.spawn(*get_rr_cmd(), codec_errors='ignore', timeout=TIMEOUT_SEC, encoding='utf-8', logfile=open('gdb_rr.log', 'w'))
child = pexpect.spawn(args[0], args[1:], codec_errors='ignore',
timeout=TIMEOUT_SEC, encoding='utf-8', logfile=open(log_file, 'w'))
child.delaybeforesend = 0
expect_gdb(r'\(rr\)')
expect_debugger(r'\(rr\)')
except Exception as e:
failed('initializing rr and gdb', e)
failed('initializing rr and debugger', e)

set_up()
43 changes: 40 additions & 3 deletions src/test/util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,28 @@ function do_ps { psflags=$1
$RR_EXE $GLOBAL_OPTIONS ps $psflags
}

# debug_lldb_only <expect-script-name> [replay-args]
#
# Load the "expect" script to drive replay of the recording of |exe|.
# Only LLDB is tested.
function debug_lldb_only { expectscript=$1; replayargs=$2
_RR_TRACE_DIR="$workdir" test-monitor $TIMEOUT debug.err \
python3 $TESTDIR/$expectscript.py --lldb \
$RR_EXE $GLOBAL_OPTIONS replay -o-S -o$TESTDIR/test_setup.lldb $replayargs
if [[ $? == 0 ]]; then
passed_msg lldb
else
failed "debug script failed (lldb)"
echo "--------------------------------------------------"
echo "lldb_rr.log:"
cat lldb_rr.log
echo "--------------------------------------------------"
echo "debug.err:"
cat debug.err
echo "--------------------------------------------------"
fi
}

# debug_gdb_only <expect-script-name> [replay-args]
#
# Load the "expect" script to drive replay of the recording of |exe|.
Expand All @@ -345,9 +367,9 @@ function debug_gdb_only { expectscript=$1; replayargs=$2
python3 $TESTDIR/$expectscript.py \
$RR_EXE $GLOBAL_OPTIONS replay -o-n -o-ix -o$TESTDIR/test_setup.gdb $replayargs
if [[ $? == 0 ]]; then
passed
passed_msg gdb
else
failed "debug script failed"
failed "debug script failed (lldb)"
echo "--------------------------------------------------"
echo "gdb_rr.log:"
cat gdb_rr.log
Expand All @@ -367,6 +389,10 @@ function passed {
echo "Test '$TESTNAME' PASSED"
}

function passed_msg { msg=$1
echo "Test '$TESTNAME' PASSED ($msg)"
}

function just_check_replay_err {
if [[ $(cat replay.err) != "" ]]; then
failed ": error during replay:"
Expand Down Expand Up @@ -470,11 +496,22 @@ function compare_test { token=$1; replayflags=$2;
check $token
}

# debug_test_gdb_only
# debug_test
#
# Record the test name passed to |util.sh|, then replay the recording
# using the "expect" script $test-name.py, which is responsible for
# computing test pass/fail.
function debug_test {
record $TESTNAME
debug_gdb_only $TEST_PREFIX$TESTNAME_NO_BITNESS
debug_lldb_only $TEST_PREFIX$TESTNAME_NO_BITNESS
}

# debug_test_gdb_only
#
# Record the test name passed to |util.sh|, then replay the recording
# using the "expect" script $test-name.py, which is responsible for
# computing test pass/fail. Only GDB is tested.
function debug_test_gdb_only {
record $TESTNAME
debug_gdb_only $TEST_PREFIX$TESTNAME_NO_BITNESS
Expand Down

0 comments on commit db8ae5f

Please sign in to comment.