diff --git a/lldb/test/Shell/Reproducer/lit.local.cfg b/lldb/test/Shell/Reproducer/lit.local.cfg --- a/lldb/test/Shell/Reproducer/lit.local.cfg +++ b/lldb/test/Shell/Reproducer/lit.local.cfg @@ -2,5 +2,9 @@ if 'LLVM_DISABLE_CRASH_REPORT' in config.environment: del config.environment['LLVM_DISABLE_CRASH_REPORT'] +# Unset the always capture environment override. if 'LLDB_CAPTURE_REPRODUCER' in config.environment: del config.environment['LLDB_CAPTURE_REPRODUCER'] + +if config.skip_reproducer_test: + config.unsupported = True diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -9,6 +9,11 @@ from lit.llvm.subst import FindTool from lit.llvm.subst import ToolSubst + +def _get_lldb_init_path(config): + return os.path.join(config.test_exec_root, 'Shell', 'lit-lldb-init') + + def use_lldb_substitutions(config): # Set up substitutions for primary tools. These tools must come from config.lldb_tools_dir # which is basically the build output directory. We do not want to find these in path or @@ -29,7 +34,7 @@ if config.llvm_libs_dir: build_script_args.append('--libs-dir={0}'.format(config.llvm_libs_dir)) - lldb_init = os.path.join(config.test_exec_root, 'Shell', 'lit-lldb-init') + lldb_init = _get_lldb_init_path(config) primary_tools = [ ToolSubst('%lldb', @@ -135,3 +140,18 @@ 'llvm-objcopy', 'lli'] additional_tool_dirs += [config.lldb_tools_dir, config.llvm_tools_dir] llvm_config.add_tool_substitutions(support_tools, additional_tool_dirs) + + +def use_lldb_repro_substitutions(config, mode): + lldb_init = _get_lldb_init_path(config) + substitutions = [ + ToolSubst( + '%lldb', + command=FindTool('lldb-repro'), + extra_args=[mode, '--no-lldbinit', '-S', lldb_init]), + ToolSubst( + '%lldb-init', + command=FindTool('lldb-repro'), + extra_args=[mode, '-S', lldb_init]), + ] + llvm_config.add_tool_substitutions(substitutions) diff --git a/lldb/test/Shell/lit.cfg.py b/lldb/test/Shell/lit.cfg.py --- a/lldb/test/Shell/lit.cfg.py +++ b/lldb/test/Shell/lit.cfg.py @@ -38,16 +38,24 @@ # test_exec_root: The root path where tests should be run. config.test_exec_root = os.path.join(config.lldb_obj_root, 'test') -# Propagate LLDB_CAPTURE_REPRODUCER +# Propagate reproducer environment vars. if 'LLDB_CAPTURE_REPRODUCER' in os.environ: config.environment['LLDB_CAPTURE_REPRODUCER'] = os.environ[ 'LLDB_CAPTURE_REPRODUCER'] +# Support running the test suite under the lldb-repro wrapper. This makes it +# possible to capture a test suite run and then rerun all the test from the +# just captured reproducer. +lldb_repro_mode = lit_config.params.get('lldb-run-with-repro', None) +if lldb_repro_mode: + config.skip_reproducer_test = True + lit_config.note("Running Shell test with lldb-repo in {} mode.".format(lldb_repro_mode)) + toolchain.use_lldb_repro_substitutions(config, lldb_repro_mode) + llvm_config.use_default_substitutions() toolchain.use_lldb_substitutions(config) toolchain.use_support_substitutions(config) - if re.match(r'^arm(hf.*-linux)|(.*-linux-gnuabihf)', config.target_triple): config.available_features.add("armhf-linux") diff --git a/lldb/utils/CMakeLists.txt b/lldb/utils/CMakeLists.txt --- a/lldb/utils/CMakeLists.txt +++ b/lldb/utils/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(lit-cpuid) add_subdirectory(lldb-dotest) +add_subdirectory(lldb-repro) diff --git a/lldb/utils/lldb-repro/CMakeLists.txt b/lldb/utils/lldb-repro/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/utils/lldb-repro/CMakeLists.txt @@ -0,0 +1,4 @@ +add_custom_target(lldb-repro) +add_dependencies(lldb-repro lldb-test-deps) +set_target_properties(lldb-repro PROPERTIES FOLDER "lldb utils") +configure_file(lldb-repro.py ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-repro COPYONLY) diff --git a/lldb/utils/lldb-repro/lldb-repro.py b/lldb/utils/lldb-repro/lldb-repro.py new file mode 100644 --- /dev/null +++ b/lldb/utils/lldb-repro/lldb-repro.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +"""lldb-repro + +lldb-repro is a utility to transparently capture and replay debugger sessions +through the command line driver. Its used to test the reproducers by running +the test suite twice. + +During the first run, with 'capture' as its first argument, it captures a +reproducer for every lldb invocation and saves it to a well-know location +derived from the arguments and current working directory. + +During the second run, with 'replay' as its first argument, the test suite is +run again but this time every invocation of lldb replays the previously +recorded session. +""" + +import sys +import os +import tempfile +import subprocess + + +def help(): + print("usage: {} capture|replay [args]".fmt(sys.argv[0])) + + +def main(): + if len(sys.argv) < 3: + help() + return 1 + + # Compute a hash based on the input arguments and the current working + # directory. + args = ' '.join(sys.argv[3:]) + cwd = os.getcwd() + input_hash = str(hash((cwd, args))) + + # Use the hash to "uniquely" identify a reproducer path. + reproducer_path = os.path.join(tempfile.gettempdir(), input_hash) + + # Create a new lldb invocation with capture or replay enabled. + lldb = os.path.join(os.path.dirname(sys.argv[0]), 'lldb') + new_args = [sys.argv[1]] + if sys.argv[2] == "replay": + new_args.extend(['--replay', reproducer_path]) + elif sys.argv[2] == "capture": + new_args.extend([ + '--capture', '--capture-path', reproducer_path, + '--reproducer-auto-generate' + ]) + new_args.extend(sys.argv[1:]) + else: + help() + return 1 + + return subprocess.call(new_args) + + +if __name__ == '__main__': + exit(main())