[lldb/Util] Add a utility to run transparently capture and replay tests.
authorJonas Devlieghere <jonas@devlieghere.com>
Mon, 20 Jan 2020 18:27:15 +0000 (10:27 -0800)
committerJonas Devlieghere <jonas@devlieghere.com>
Mon, 20 Jan 2020 18:30:19 +0000 (10:30 -0800)
This patch introduces a small new utility (lldb-repro) 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, 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, the test suite is run
again but this time every invocation of lldb replays the previously
recorded session.

Differential revision: https://reviews.llvm.org/D72823

lldb/test/Shell/Reproducer/lit.local.cfg
lldb/test/Shell/helper/toolchain.py
lldb/test/Shell/lit.cfg.py
lldb/utils/CMakeLists.txt
lldb/utils/lldb-repro/CMakeLists.txt [new file with mode: 0644]
lldb/utils/lldb-repro/lldb-repro.py [new file with mode: 0644]

index dbb37b1..51a744a 100644 (file)
@@ -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
index 9d144bb..97fdb7e 100644 (file)
@@ -9,6 +9,11 @@ from lit.llvm import llvm_config
 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 @@ def use_lldb_substitutions(config):
     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 @@ def use_support_substitutions(config):
                      '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)
index cc6155f..271ca7b 100644 (file)
@@ -38,16 +38,24 @@ config.test_source_root = os.path.dirname(__file__)
 # 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")
 
index d08f66f..00c81e6 100644 (file)
@@ -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 (file)
index 0000000..0bfcaaa
--- /dev/null
@@ -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 (file)
index 0000000..c925c47
--- /dev/null
@@ -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())