Skip to content

Commit

Permalink
[lldb/Util] Add a utility to run transparently capture and replay tests.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
JDevlieghere committed Jan 20, 2020
1 parent b37f6d3 commit 67420f1
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 3 deletions.
4 changes: 4 additions & 0 deletions lldb/test/Shell/Reproducer/lit.local.cfg
Expand Up @@ -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
22 changes: 21 additions & 1 deletion lldb/test/Shell/helper/toolchain.py
Expand Up @@ -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
Expand All @@ -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',
Expand Down Expand Up @@ -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)
12 changes: 10 additions & 2 deletions lldb/test/Shell/lit.cfg.py
Expand Up @@ -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")

Expand Down
1 change: 1 addition & 0 deletions lldb/utils/CMakeLists.txt
@@ -1,2 +1,3 @@
add_subdirectory(lit-cpuid)
add_subdirectory(lldb-dotest)
add_subdirectory(lldb-repro)
4 changes: 4 additions & 0 deletions 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)
60 changes: 60 additions & 0 deletions 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())

0 comments on commit 67420f1

Please sign in to comment.