zts: add a debug option to get full test output
The test runner accumulates output from individual tests, then writes it to the log at the end. If a test hangs or crashes the system half way through, we get no insight into how it got to where it did. This adds a -D option for "debug". When set, all test output is written to stdout. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Akash B <akash-b@hpe.com> Signed-off-by: Rob Norris <rob.norris@klarasystems.com> Closes #16096
This commit is contained in:
parent
f14a62ebbe
commit
ad8c8c1e31
|
@ -32,6 +32,7 @@ SCRIPT_COMMON=${SCRIPT_COMMON:-${0%/*}/common.sh}
|
||||||
PROG=zfs-tests.sh
|
PROG=zfs-tests.sh
|
||||||
VERBOSE="no"
|
VERBOSE="no"
|
||||||
QUIET=""
|
QUIET=""
|
||||||
|
DEBUG=""
|
||||||
CLEANUP="yes"
|
CLEANUP="yes"
|
||||||
CLEANUPALL="no"
|
CLEANUPALL="no"
|
||||||
KMSG=""
|
KMSG=""
|
||||||
|
@ -313,6 +314,7 @@ OPTIONS:
|
||||||
-h Show this message
|
-h Show this message
|
||||||
-v Verbose zfs-tests.sh output
|
-v Verbose zfs-tests.sh output
|
||||||
-q Quiet test-runner output
|
-q Quiet test-runner output
|
||||||
|
-D Debug; show all test output immediately (noisy)
|
||||||
-x Remove all testpools, dm, lo, and files (unsafe)
|
-x Remove all testpools, dm, lo, and files (unsafe)
|
||||||
-k Disable cleanup after test failure
|
-k Disable cleanup after test failure
|
||||||
-K Log test names to /dev/kmsg
|
-K Log test names to /dev/kmsg
|
||||||
|
@ -351,7 +353,7 @@ $0 -x
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
while getopts 'hvqxkKfScRmn:d:s:r:?t:T:u:I:' OPTION; do
|
while getopts 'hvqxkKfScRmn:d:Ds:r:?t:T:u:I:' OPTION; do
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
h)
|
h)
|
||||||
usage
|
usage
|
||||||
|
@ -397,6 +399,9 @@ while getopts 'hvqxkKfScRmn:d:s:r:?t:T:u:I:' OPTION; do
|
||||||
d)
|
d)
|
||||||
FILEDIR="$OPTARG"
|
FILEDIR="$OPTARG"
|
||||||
;;
|
;;
|
||||||
|
D)
|
||||||
|
DEBUG="yes"
|
||||||
|
;;
|
||||||
I)
|
I)
|
||||||
ITERATIONS="$OPTARG"
|
ITERATIONS="$OPTARG"
|
||||||
if [ "$ITERATIONS" -le 0 ]; then
|
if [ "$ITERATIONS" -le 0 ]; then
|
||||||
|
@ -691,6 +696,7 @@ REPORT_FILE=$(mktemp_file zts-report)
|
||||||
#
|
#
|
||||||
msg "${TEST_RUNNER}" \
|
msg "${TEST_RUNNER}" \
|
||||||
"${QUIET:+-q}" \
|
"${QUIET:+-q}" \
|
||||||
|
"${DEBUG:+-D}" \
|
||||||
"${KMEMLEAK:+-m}" \
|
"${KMEMLEAK:+-m}" \
|
||||||
"${KMSG:+-K}" \
|
"${KMSG:+-K}" \
|
||||||
"-c \"${RUNFILES}\"" \
|
"-c \"${RUNFILES}\"" \
|
||||||
|
@ -700,6 +706,7 @@ msg "${TEST_RUNNER}" \
|
||||||
{ PATH=$STF_PATH \
|
{ PATH=$STF_PATH \
|
||||||
${TEST_RUNNER} \
|
${TEST_RUNNER} \
|
||||||
${QUIET:+-q} \
|
${QUIET:+-q} \
|
||||||
|
${DEBUG:+-D} \
|
||||||
${KMEMLEAK:+-m} \
|
${KMEMLEAK:+-m} \
|
||||||
${KMSG:+-K} \
|
${KMSG:+-K} \
|
||||||
-c "${RUNFILES}" \
|
-c "${RUNFILES}" \
|
||||||
|
@ -726,6 +733,7 @@ if [ "$RESULT" -eq "2" ] && [ -n "$RERUN" ]; then
|
||||||
{ PATH=$STF_PATH \
|
{ PATH=$STF_PATH \
|
||||||
${TEST_RUNNER} \
|
${TEST_RUNNER} \
|
||||||
${QUIET:+-q} \
|
${QUIET:+-q} \
|
||||||
|
${DEBUG:+-D} \
|
||||||
${KMEMLEAK:+-m} \
|
${KMEMLEAK:+-m} \
|
||||||
-c "${RUNFILES}" \
|
-c "${RUNFILES}" \
|
||||||
-T "${TAGS}" \
|
-T "${TAGS}" \
|
||||||
|
|
|
@ -113,8 +113,9 @@ class Output(object):
|
||||||
This class is a slightly modified version of the 'Stream' class found
|
This class is a slightly modified version of the 'Stream' class found
|
||||||
here: http://goo.gl/aSGfv
|
here: http://goo.gl/aSGfv
|
||||||
"""
|
"""
|
||||||
def __init__(self, stream):
|
def __init__(self, stream, debug=False):
|
||||||
self.stream = stream
|
self.stream = stream
|
||||||
|
self.debug = debug
|
||||||
self._buf = b''
|
self._buf = b''
|
||||||
self.lines = []
|
self.lines = []
|
||||||
|
|
||||||
|
@ -140,6 +141,8 @@ class Output(object):
|
||||||
buf = os.read(fd, 4096)
|
buf = os.read(fd, 4096)
|
||||||
if not buf:
|
if not buf:
|
||||||
return None
|
return None
|
||||||
|
if self.debug:
|
||||||
|
os.write(sys.stderr.fileno(), buf)
|
||||||
if b'\n' not in buf:
|
if b'\n' not in buf:
|
||||||
self._buf += buf
|
self._buf += buf
|
||||||
return []
|
return []
|
||||||
|
@ -238,14 +241,14 @@ User: %s
|
||||||
ret = '%s -E -u %s %s' % (SUDO, user, cmd)
|
ret = '%s -E -u %s %s' % (SUDO, user, cmd)
|
||||||
return ret.split(' ')
|
return ret.split(' ')
|
||||||
|
|
||||||
def collect_output(self, proc):
|
def collect_output(self, proc, debug=False):
|
||||||
"""
|
"""
|
||||||
Read from stdout/stderr as data becomes available, until the
|
Read from stdout/stderr as data becomes available, until the
|
||||||
process is no longer running. Return the lines from the stdout and
|
process is no longer running. Return the lines from the stdout and
|
||||||
stderr Output objects.
|
stderr Output objects.
|
||||||
"""
|
"""
|
||||||
out = Output(proc.stdout)
|
out = Output(proc.stdout, debug)
|
||||||
err = Output(proc.stderr)
|
err = Output(proc.stderr, debug)
|
||||||
res = []
|
res = []
|
||||||
while proc.returncode is None:
|
while proc.returncode is None:
|
||||||
proc.poll()
|
proc.poll()
|
||||||
|
@ -308,7 +311,10 @@ User: %s
|
||||||
|
|
||||||
try:
|
try:
|
||||||
t.start()
|
t.start()
|
||||||
self.result.stdout, self.result.stderr = self.collect_output(proc)
|
|
||||||
|
out, err = self.collect_output(proc, options.debug)
|
||||||
|
self.result.stdout = out
|
||||||
|
self.result.stderr = err
|
||||||
|
|
||||||
if kmemleak:
|
if kmemleak:
|
||||||
cmd = f'{SUDO} sh -c "echo scan > {KMEMLEAK_FILE}"'
|
cmd = f'{SUDO} sh -c "echo scan > {KMEMLEAK_FILE}"'
|
||||||
|
@ -624,7 +630,7 @@ Tags: %s
|
||||||
|
|
||||||
|
|
||||||
class TestRun(object):
|
class TestRun(object):
|
||||||
props = ['quiet', 'outputdir']
|
props = ['quiet', 'outputdir', 'debug']
|
||||||
|
|
||||||
def __init__(self, options):
|
def __init__(self, options):
|
||||||
self.tests = {}
|
self.tests = {}
|
||||||
|
@ -644,7 +650,8 @@ class TestRun(object):
|
||||||
('post_user', ''),
|
('post_user', ''),
|
||||||
('failsafe', ''),
|
('failsafe', ''),
|
||||||
('failsafe_user', ''),
|
('failsafe_user', ''),
|
||||||
('tags', [])
|
('tags', []),
|
||||||
|
('debug', False)
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -1067,6 +1074,8 @@ def parse_args():
|
||||||
help='Specify tests to run via config files.')
|
help='Specify tests to run via config files.')
|
||||||
parser.add_option('-d', action='store_true', default=False, dest='dryrun',
|
parser.add_option('-d', action='store_true', default=False, dest='dryrun',
|
||||||
help='Dry run. Print tests, but take no other action.')
|
help='Dry run. Print tests, but take no other action.')
|
||||||
|
parser.add_option('-D', action='store_true', default=False, dest='debug',
|
||||||
|
help='Write all test output to stdout as it arrives.')
|
||||||
parser.add_option('-l', action='callback', callback=options_cb,
|
parser.add_option('-l', action='callback', callback=options_cb,
|
||||||
default=None, dest='logfile', metavar='logfile',
|
default=None, dest='logfile', metavar='logfile',
|
||||||
type='string',
|
type='string',
|
||||||
|
|
Loading…
Reference in New Issue