Index: lldb/bindings/headers.swig =================================================================== --- lldb/bindings/headers.swig +++ lldb/bindings/headers.swig @@ -47,6 +47,7 @@ #include "lldb/API/SBProcessInfo.h" #include "lldb/API/SBQueue.h" #include "lldb/API/SBQueueItem.h" +#include "lldb/API/SBReproducer.h" #include "lldb/API/SBSection.h" #include "lldb/API/SBSourceManager.h" #include "lldb/API/SBStream.h" Index: lldb/bindings/interface/SBReproducer.i =================================================================== --- /dev/null +++ lldb/bindings/interface/SBReproducer.i @@ -0,0 +1,17 @@ +//===-- SWIG Interface for SBReproducer--------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +namespace lldb { +class SBReproducer +{ + public: + static const char *Capture(const char *path); + static const char *APIReplay(const char *path); + static bool SetAutoGenerate(bool b); +}; +} Index: lldb/bindings/interfaces.swig =================================================================== --- lldb/bindings/interfaces.swig +++ lldb/bindings/interfaces.swig @@ -54,6 +54,7 @@ %include "./interface/SBProcessInfo.i" %include "./interface/SBQueue.i" %include "./interface/SBQueueItem.i" +%include "./interface/SBReproducer.i" %include "./interface/SBSection.i" %include "./interface/SBSourceManager.i" %include "./interface/SBStream.i" Index: lldb/include/lldb/API/SBReproducer.h =================================================================== --- lldb/include/lldb/API/SBReproducer.h +++ lldb/include/lldb/API/SBReproducer.h @@ -22,6 +22,7 @@ static const char *Capture(const char *path); static const char *Replay(const char *path); static const char *Replay(const char *path, bool skip_version_check); + static const char *APIReplay(const char *path); static const char *GetPath(); static bool SetAutoGenerate(bool b); static bool Generate(); Index: lldb/packages/Python/lldbsuite/test/configuration.py =================================================================== --- lldb/packages/Python/lldbsuite/test/configuration.py +++ lldb/packages/Python/lldbsuite/test/configuration.py @@ -87,7 +87,6 @@ # Set this flag if there is any session info dumped during the test run. sdir_has_content = False - # svn_info stores the output from 'svn info lldb.base.dir'. svn_info = '' @@ -124,6 +123,10 @@ results_formatter_options = None test_result = None +# Reproducers +capture_path = None +replay_path = None + # Test rerun configuration vars rerun_all_issues = False Index: lldb/packages/Python/lldbsuite/test/decorators.py =================================================================== --- lldb/packages/Python/lldbsuite/test/decorators.py +++ lldb/packages/Python/lldbsuite/test/decorators.py @@ -854,3 +854,11 @@ return "ASAN unsupported" return None return skipTestIfFn(is_asan)(func) + +def skipIfReproducer(func): + """Skip this test if the environment is set up to run LLDB with reproducers.""" + def is_reproducer(): + if ('LLDB_REPRODUCER_CAPTURE_PATH' in os.environ or 'LLDB_REPRODUCER_REPLAY_PATH' in os.environ): + return "reproducers unsupported" + return None + return skipTestIfFn(is_reproducer)(func) Index: lldb/packages/Python/lldbsuite/test/dotest.py =================================================================== --- lldb/packages/Python/lldbsuite/test/dotest.py +++ lldb/packages/Python/lldbsuite/test/dotest.py @@ -429,6 +429,17 @@ configuration.results_formatter_name = ( "lldbsuite.test_event.formatter.results_formatter.ResultsFormatter") + # Reproducer arguments + if args.capture_path and args.replay_path: + logging.error('Cannot specify both a capture and a replay path.') + sys.exit(-1) + + if args.capture_path: + configuration.capture_path = args.capture_path + + if args.replay_path: + configuration.replay_path = args.replay_path + # rerun-related arguments configuration.rerun_all_issues = args.rerun_all_issues @@ -955,8 +966,19 @@ setupSysPath() import lldbconfig + if configuration.capture_path or configuration.replay_path: + lldbconfig.INITIALIZE = False import lldb + if configuration.capture_path: + lldb.SBReproducer.Capture(configuration.capture_path) + lldb.SBReproducer.SetAutoGenerate(True) + elif configuration.replay_path: + lldb.SBReproducer.APIReplay(configuration.replay_path) + + if not lldbconfig.INITIALIZE: + lldb.SBDebugger.Initialize() + # Use host platform by default. lldb.selected_platform = lldb.SBPlatform.GetHostPlatform() Index: lldb/packages/Python/lldbsuite/test/dotest_args.py =================================================================== --- lldb/packages/Python/lldbsuite/test/dotest_args.py +++ lldb/packages/Python/lldbsuite/test/dotest_args.py @@ -196,6 +196,17 @@ metavar='platform-working-dir', help='The directory to use on the remote platform.') + # Reproducer options + group = parser.add_argument_group('Reproducer options') + group.add_argument( + '--capture-path', + metavar='reproducer path', + help='The reproducer capture path') + group.add_argument( + '--replay-path', + metavar='reproducer path', + help='The reproducer replay path') + # Test-suite behaviour group = parser.add_argument_group('Runtime behaviour options') X('-d', 'Suspend the process after launch to wait indefinitely for a debugger to attach') Index: lldb/source/API/SBReproducer.cpp =================================================================== --- lldb/source/API/SBReproducer.cpp +++ lldb/source/API/SBReproducer.cpp @@ -124,6 +124,15 @@ return nullptr; } +const char *SBReproducer::APIReplay(const char *path) { + static std::string error; + if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) { + error = llvm::toString(std::move(e)); + return error.c_str(); + } + return nullptr; +} + const char *SBReproducer::Replay(const char *path) { return SBReproducer::Replay(path, false); } Index: lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py =================================================================== --- lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py +++ lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py @@ -20,6 +20,7 @@ @skipIfWindows @skipIfRemote @skipIfiOSSimulator + @skipIfReproducer def test_reproducer_attach(self): """Test thread creation after process attach.""" exe = '%s_%d' % (self.testMethodName, os.getpid()) Index: lldb/test/API/lit.cfg.py =================================================================== --- lldb/test/API/lit.cfg.py +++ lldb/test/API/lit.cfg.py @@ -60,6 +60,17 @@ 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: + lit_config.note("Running Shell test with lldb-repo in {} mode.".format(lldb_repro_mode)) + if lldb_repro_mode == 'capture': + config.available_features.add('lldb-repro-capture') + elif lldb_repro_mode == 'replay': + config.available_features.add('lldb-repro-replay') + # Clean the module caches in the test build directory. This is necessary in an # incremental build whenever clang changes underneath, so doing it once per # lit.py invocation is close enough. Index: lldb/test/API/lldbtest.py =================================================================== --- lldb/test/API/lldbtest.py +++ lldb/test/API/lldbtest.py @@ -1,6 +1,6 @@ from __future__ import absolute_import import os - +import tempfile import subprocess import sys @@ -86,6 +86,14 @@ shutil.copy(python, copied_python) cmd[0] = copied_python + reproducer_path = os.path.join(tempfile.gettempdir(), testFile) + if 'lldb-repro-capture' in test.config.available_features: + test.config.environment[ + 'LLDB_REPRODUCER_CAPTURE_PATH'] = reproducer_path + elif 'lldb-repro-replay' in test.config.available_features: + test.config.environment[ + 'LLDB_REPRODUCER_REPLAY_PATH'] = reproducer_path + timeoutInfo = None try: out, err, exitCode = lit.util.executeCommand(