[lldb/Reproducers] Capture reproducers from the API test suite.
authorJonas Devlieghere <jonas@devlieghere.com>
Tue, 14 Apr 2020 15:41:50 +0000 (08:41 -0700)
committerJonas Devlieghere <jonas@devlieghere.com>
Tue, 14 Apr 2020 16:24:23 +0000 (09:24 -0700)
Make it possible to capture reproducers from the API test suite. Given
the symmetry between capture and replay, this patch also adds the
necessary code for replay. For now this is a NO-OP until the
corresponding reproducer instrumentation changes land.

For more info please refer to the RFC on lldb-dev:
http://lists.llvm.org/pipermail/lldb-dev/2020-April/016100.html

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

12 files changed:
lldb/bindings/headers.swig
lldb/bindings/interface/SBReproducer.i [new file with mode: 0644]
lldb/bindings/interfaces.swig
lldb/include/lldb/API/SBReproducer.h
lldb/packages/Python/lldbsuite/test/configuration.py
lldb/packages/Python/lldbsuite/test/decorators.py
lldb/packages/Python/lldbsuite/test/dotest.py
lldb/packages/Python/lldbsuite/test/dotest_args.py
lldb/source/API/SBReproducer.cpp
lldb/test/API/functionalities/reproducers/attach/TestReproducerAttach.py
lldb/test/API/lit.cfg.py
lldb/test/API/lldbtest.py

index 7371e1a..42ccd36 100644 (file)
@@ -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"
diff --git a/lldb/bindings/interface/SBReproducer.i b/lldb/bindings/interface/SBReproducer.i
new file mode 100644 (file)
index 0000000..e976863
--- /dev/null
@@ -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 *PassiveReplay(const char *path);
+        static bool SetAutoGenerate(bool b);
+};
+}
index e906bb9..025a3e3 100644 (file)
@@ -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 0a25bcb..5a298d1 100644 (file)
@@ -22,6 +22,7 @@ public:
   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 *PassiveReplay(const char *path);
   static const char *GetPath();
   static bool SetAutoGenerate(bool b);
   static bool Generate();
index ce6f46a..9d69438 100644 (file)
@@ -87,7 +87,6 @@ session_file_format = 'fnmac'
 
 # 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_object = None
 results_formatter_options = None
 test_result = None
 
+# Reproducers
+capture_path = None
+replay_path = None
+
 # Test rerun configuration vars
 rerun_all_issues = False
 
index cc28ae9..65b63b4 100644 (file)
@@ -854,3 +854,11 @@ def skipIfAsan(func):
             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 configuration.capture_path or configuration.replay_path:
+            return "reproducers unsupported"
+        return None
+    return skipTestIfFn(is_reproducer)(func)
index 31c617c..b5666ec 100644 (file)
@@ -429,6 +429,17 @@ def parseOptionsAndInitTestdirs():
         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 @@ def run_suite():
     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.PassiveReplay(configuration.replay_path)
+
+    if not lldbconfig.INITIALIZE:
+        lldb.SBDebugger.Initialize()
+
     # Use host platform by default.
     lldb.selected_platform = lldb.SBPlatform.GetHostPlatform()
 
index 385f65f..ad9508d 100644 (file)
@@ -196,6 +196,17 @@ def create_parser():
         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 15ad887..ba91279 100644 (file)
@@ -124,6 +124,15 @@ const char *SBReproducer::Capture(const char *path) {
   return nullptr;
 }
 
+const char *SBReproducer::PassiveReplay(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 968268d..70f8d58 100644 (file)
@@ -20,6 +20,7 @@ class ReproducerAttachTestCase(TestBase):
     @skipIfWindows
     @skipIfRemote
     @skipIfiOSSimulator
+    @skipIfReproducer
     def test_reproducer_attach(self):
         """Test thread creation after process attach."""
         exe = '%s_%d' % (self.testMethodName, os.getpid())
index 10df560..835e69e 100644 (file)
@@ -60,6 +60,17 @@ 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:
+  lit_config.note("Running API tests 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 94c0588..0831af6 100644 (file)
@@ -1,6 +1,6 @@
 from __future__ import absolute_import
 import os
-
+import tempfile
 import subprocess
 import sys
 
@@ -67,14 +67,15 @@ class LLDBTest(TestFormat):
         # python exe as the first parameter of the command.
         cmd = [sys.executable] + self.dotest_cmd + [testPath, '-p', testFile]
 
+        builddir = getBuildDir(cmd)
+        mkdir_p(builddir)
+
         # The macOS system integrity protection (SIP) doesn't allow injecting
         # libraries into system binaries, but this can be worked around by
         # copying the binary into a different location.
         if 'DYLD_INSERT_LIBRARIES' in test.config.environment and \
                 (sys.executable.startswith('/System/') or \
                 sys.executable.startswith('/usr/bin/')):
-            builddir = getBuildDir(cmd)
-            mkdir_p(builddir)
             copied_python = os.path.join(builddir, 'copied-system-python')
             if not os.path.isfile(copied_python):
                 import shutil, subprocess
@@ -86,6 +87,16 @@ class LLDBTest(TestFormat):
                 shutil.copy(python, copied_python)
             cmd[0] = copied_python
 
+        if 'lldb-repro-capture' in test.config.available_features or \
+           'lldb-repro-replay' in test.config.available_features:
+            reproducer_root = os.path.join(builddir, 'reproducers')
+            mkdir_p(reproducer_root)
+            reproducer_path = os.path.join(reproducer_root, testFile)
+            if 'lldb-repro-capture' in test.config.available_features:
+                cmd.extend(['--capture-path', reproducer_path])
+            else:
+                cmd.extend(['--replay-path', reproducer_path])
+
         timeoutInfo = None
         try:
             out, err, exitCode = lit.util.executeCommand(