Files
scylladb/test/scylla_gdb/run
Avi Kivity 0ba3ce1741 test: gdb: avoid using file(1) to determine if debug information is present
The scylla_gdb tests verify, as a sanity check, that the executable
was built with debug information. They do so via file(1).

In Fedora 42, file(1) crashes on ELF files that have interpreter pathnames
larger than 128 characters[1]. This was later fixed[2], but the fix is not
in any release.

Work around the problem by using objdump instead of file.

[1] https://bugzilla.redhat.com/show_bug.cgi?id=2354970
[2] b3384a1fbf

Closes scylladb/scylladb#23823
2025-04-21 13:29:27 +03:00

91 lines
3.5 KiB
Python
Executable File

#!/usr/bin/env python3
import sys
import os
import subprocess
# Use the run.py library from ../cqlpy:
sys.path.insert(1, sys.path[0] + '/../cqlpy')
import run
print('Scylla is: ' + run.find_scylla() + '.')
# Gdb will only work if the executable was built with debug symbols
# (e.g., Scylla's release or debug build modes mode, but not dev mode).
# Check this quickly up-front, instead of waiting for gdb to fail in a
# mysterious way when it can't look up types.
if not '.debug_info' in subprocess.run(['objdump', '-h', run.scylla],
capture_output=True, text=True).stdout:
print(f'Scylla executable was compiled without debugging information (-g)')
print(f'so cannot be used to test gdb. Please set SCYLLA environment variable.')
exit(1)
# Run Scylla, waiting until it can respond to CQL
pid = run.run_with_temporary_dir(run.run_scylla_cmd)
ip = run.pid_to_ip(pid)
run.wait_for_services(pid, [lambda: run.check_cql(ip)])
# We do something strange here: We start pytest *inside* gdb's Python
# interpreter. This will allow us to test various gdb commands added
# by scylla-gdb.py inside gdb using the pytest framework.
# TODO: consider a more straightforward implementation, where we don't
# run pytest inside gdb - and instead run gdb as a separate process and
# pytest just sends commands to it.
# TODO: think if we can avoid code duplication with run.run_ptest().
def run_pytest_in_gdb(pytest_dir, executable, additional_parameters):
sys.stdout.flush()
sys.stderr.flush()
pid = os.fork()
if pid == 0:
# child:
run.run_with_temporary_dir_pids = set() # no children to clean up on child
run.run_pytest_pids = set()
os.chdir(pytest_dir)
pytest_args = ['-o', 'junit_family=xunit2'] + additional_parameters
pytest_cmd = f'print("Starting pytest {" ".join(pytest_args)}"); import pytest; sys.argv[0]="pytest"; sys.exit(pytest.main({str(pytest_args)}))'
print(f'Starting gdb {executable}')
sys.stdout.flush()
args = ['gdb',
'-batch', '-n',
'-ex', 'set python print-stack full',
'-ex', 'python ' + pytest_cmd,
]
if executable:
args += ['-se', executable]
os.execvp('gdb', args)
exit(1)
# parent:
run.run_pytest_pids.add(pid)
if os.waitpid(pid, 0)[1]:
return False
else:
return True
# Interesting note: We must use "--scylla-tmp-dir=DIR" here instead of
# "--scylla-tmp-dir DIR": While the latter does work, pytest has a bug that
# its command-line parser finds the given directory name in the original
# command line, saves it as "initialpaths", and uses it to print what it
# thinks are nice (but are really incorrect) relative paths for "nodes" (test
# source files).
success = True
for with_scylla in [True, False]:
if with_scylla:
args = ['--scylla-pid='+str(pid),
'--scylla-tmp-dir='+run.pid_to_dir(pid),
'-m', 'not without_scylla']
executable = run.scylla
else:
args = ['-m', 'without_scylla']
executable = ''
if not run_pytest_in_gdb(sys.path[0], executable, args + sys.argv[1:]):
success = False
run.summary = 'Scylla GDB tests pass' if success else 'Scylla GDB tests failure'
exit(0 if success else 1)
# Note that the run.cleanup_all() function runs now, just like on any exit
# for any reason in this script. It will delete the temporary files and
# announce the failure or success of the test (printing run.summary).