diff --git a/libcxx/utils/libcxx/test/config.py b/libcxx/utils/libcxx/test/config.py --- a/libcxx/utils/libcxx/test/config.py +++ b/libcxx/utils/libcxx/test/config.py @@ -624,6 +624,11 @@ '--codesign_identity "{}"'.format(codesign_ident), '--env {}'.format(env_vars) ] + # The exec_nowrapper substitution is used for configuration checks. + sub.append(('%{exec_nowrapper}', '{} {} -- '.format(self.executor, ' '.join(exec_args)))) + if self.lit_config.run_with_debugger: + exec_args.append("--wrapper-command") + exec_args.append(pipes.quote(' '.join(map(pipes.quote, self.lit_config.debugger_args)))) sub.append(('%{exec}', '{} {} -- '.format(self.executor, ' '.join(exec_args)))) if self.get_lit_conf('libcxx_gdb'): sub.append(('%{libcxx_gdb}', self.get_lit_conf('libcxx_gdb'))) diff --git a/libcxx/utils/libcxx/test/dsl.py b/libcxx/utils/libcxx/test/dsl.py --- a/libcxx/utils/libcxx/test/dsl.py +++ b/libcxx/utils/libcxx/test/dsl.py @@ -109,8 +109,10 @@ if exitCode != 0: return None + # Note: We use exec_nowrapper here to avoid running the configuration + # tests under GDB/valgrind/etc. out, err, exitCode, _ = _executeScriptInternal(test, [ - "%{{exec}} %t.exe {}".format(' '.join(args)) + "%{{exec_nowrapper}} %t.exe {}".format(' '.join(args)) ]) if exitCode != 0: return None diff --git a/llvm/utils/lit/lit/LitConfig.py b/llvm/utils/lit/lit/LitConfig.py --- a/llvm/utils/lit/lit/LitConfig.py +++ b/llvm/utils/lit/lit/LitConfig.py @@ -1,14 +1,15 @@ from __future__ import absolute_import + import inspect import os -import platform import sys import lit.Test -import lit.formats import lit.TestingConfig +import lit.formats import lit.util + # LitConfig must be a new style class for properties to work class LitConfig(object): """LitConfig - Configuration data for a 'lit' test runner instance, shared @@ -26,6 +27,7 @@ params, config_prefix = None, maxIndividualTestTime = 0, parallelism_groups = {}, + run_with_debugger = None, echo_all_commands = False): # The name of the test runner. self.progname = progname @@ -63,6 +65,17 @@ self.valgrindArgs.append('--leak-check=no') self.valgrindArgs.extend(self.valgrindUserArgs) + self.run_with_debugger = run_with_debugger is not None + self.debugger_args = [] + if self.run_with_debugger: + if run_with_debugger == 'gdb': + # We use "thread apply all bt" instead of "bt" to ensure that a + # successful exit doesn't cause GDB to return a non-zero exit code + # due the program no longer existing when "bt" is executed. + self.debugger_args = ["gdb", "--quiet", "--batch", "--return-child-result", + "-ex=r", "-ex=thread apply all bt", "--args"] + else: + raise ValueError("Unsupported debugger: " + run_with_debugger) self.maxIndividualTestTime = maxIndividualTestTime self.parallelism_groups = parallelism_groups self.echo_all_commands = echo_all_commands diff --git a/llvm/utils/lit/lit/cl_arguments.py b/llvm/utils/lit/lit/cl_arguments.py --- a/llvm/utils/lit/lit/cl_arguments.py +++ b/llvm/utils/lit/lit/cl_arguments.py @@ -171,6 +171,15 @@ debug_group.add_argument("--show-used-features", help="Show all features used in the test suite (in XFAIL, UNSUPPORTED and REQUIRES) and exit", action="store_true") + # TODO: Also support lldb + debug_group.add_argument("--run-with-debugger", + help="Run tests with gdb and print a backtrace on crash. " + "This is currently only supported for libc++/libunwind.", + choices=("gdb", )) + debug_group.add_argument("--gdb", + dest="run_with_debugger", + help="This is an alias for --run-with-debugger=gdb.", + action="store_const", const="gdb") # LIT is special: environment variables override command line arguments. env_args = shlex.split(os.environ.get("LIT_OPTS", "")) diff --git a/llvm/utils/lit/lit/main.py b/llvm/utils/lit/lit/main.py --- a/llvm/utils/lit/lit/main.py +++ b/llvm/utils/lit/lit/main.py @@ -37,6 +37,7 @@ isWindows=is_windows, params=params, config_prefix=opts.configPrefix, + run_with_debugger=opts.run_with_debugger, echo_all_commands=opts.echoAllCommands) discovered_tests = lit.discovery.find_tests_for_inputs(lit_config, opts.test_paths)