[Lldb-commits] [lldb] r346780 - Add GDB remote packet reproducer.

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 13 11:18:16 PST 2018


Author: jdevlieghere
Date: Tue Nov 13 11:18:16 2018
New Revision: 346780

URL: http://llvm.org/viewvc/llvm-project?rev=346780&view=rev
Log:
Add GDB remote packet reproducer.

Added:
    lldb/trunk/include/lldb/Utility/Reproducer.h
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py
    lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/main.c
    lldb/trunk/source/Commands/CommandObjectReproducer.cpp
    lldb/trunk/source/Commands/CommandObjectReproducer.h
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
    lldb/trunk/source/Utility/Reproducer.cpp
Modified:
    lldb/trunk/include/lldb/API/SBDebugger.h
    lldb/trunk/include/lldb/Core/Debugger.h
    lldb/trunk/include/lldb/Host/HostInfoBase.h
    lldb/trunk/scripts/interface/SBDebugger.i
    lldb/trunk/source/API/SBDebugger.cpp
    lldb/trunk/source/Commands/CMakeLists.txt
    lldb/trunk/source/Core/Debugger.cpp
    lldb/trunk/source/Host/common/HostInfoBase.cpp
    lldb/trunk/source/Interpreter/CommandInterpreter.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/CMakeLists.txt
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
    lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
    lldb/trunk/source/Utility/CMakeLists.txt
    lldb/trunk/tools/driver/Driver.cpp
    lldb/trunk/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
    lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
    lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
    lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp
    lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.h

Modified: lldb/trunk/include/lldb/API/SBDebugger.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBDebugger.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/include/lldb/API/SBDebugger.h (original)
+++ lldb/trunk/include/lldb/API/SBDebugger.h Tue Nov 13 11:18:16 2018
@@ -109,7 +109,7 @@ public:
                                              const char *archname);
 
   lldb::SBTarget CreateTarget(const char *filename);
-  
+
   lldb::SBTarget GetDummyTarget();
 
   // Return true if target is deleted from the target list of the debugger.
@@ -226,6 +226,10 @@ public:
 
   void SetPrompt(const char *prompt);
 
+  const char *GetReproducerPath() const;
+
+  void SetReproducerPath(const char *reproducer);
+
   lldb::ScriptLanguage GetScriptLanguage() const;
 
   void SetScriptLanguage(lldb::ScriptLanguage script_lang);

Modified: lldb/trunk/include/lldb/Core/Debugger.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/Debugger.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/Debugger.h (original)
+++ lldb/trunk/include/lldb/Core/Debugger.h Tue Nov 13 11:18:16 2018
@@ -261,6 +261,11 @@ public:
   void SetPrompt(llvm::StringRef p);
   void SetPrompt(const char *) = delete;
 
+  llvm::StringRef GetReproducerPath() const;
+
+  void SetReproducerPath(llvm::StringRef p);
+  void SetReproducerPath(const char *) = delete;
+
   bool GetUseExternalEditor() const;
 
   bool SetUseExternalEditor(bool use_external_editor_p);

Modified: lldb/trunk/include/lldb/Host/HostInfoBase.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Host/HostInfoBase.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Host/HostInfoBase.h (original)
+++ lldb/trunk/include/lldb/Host/HostInfoBase.h Tue Nov 13 11:18:16 2018
@@ -93,6 +93,12 @@ public:
   /// FileSpec is filled in.
   static FileSpec GetGlobalTempDir();
 
+  /// Returns the reproducer temporary directory. This directory will **not**
+  /// be automatically cleaned up when this process exits, but might be removed
+  /// by the reproducer generator. Only the directory member of the FileSpec is
+  /// filled in.
+  static FileSpec GetReproducerTempDir();
+
   //---------------------------------------------------------------------------
   /// If the triple does not specify the vendor, os, and environment parts, we
   /// "augment" these using information from the host and return the resulting
@@ -105,6 +111,7 @@ protected:
   static bool ComputeSupportExeDirectory(FileSpec &file_spec);
   static bool ComputeProcessTempFileDirectory(FileSpec &file_spec);
   static bool ComputeGlobalTempFileDirectory(FileSpec &file_spec);
+  static bool ComputeReproducerTempFileDirectory(FileSpec &file_spec);
   static bool ComputeTempFileBaseDirectory(FileSpec &file_spec);
   static bool ComputeHeaderDirectory(FileSpec &file_spec);
   static bool ComputeSystemPluginsDirectory(FileSpec &file_spec);

Added: lldb/trunk/include/lldb/Utility/Reproducer.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Utility/Reproducer.h?rev=346780&view=auto
==============================================================================
--- lldb/trunk/include/lldb/Utility/Reproducer.h (added)
+++ lldb/trunk/include/lldb/Utility/Reproducer.h Tue Nov 13 11:18:16 2018
@@ -0,0 +1,175 @@
+//===-- Reproducer.h --------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_REPRODUCER_H
+#define LLDB_UTILITY_REPRODUCER_H
+
+#include "lldb/Utility/FileSpec.h"
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/YAMLTraits.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace lldb_private {
+namespace repro {
+
+class Reproducer;
+
+/// Abstraction for information associated with a provider. This information
+/// is serialized into an index which is used by the loader.
+struct ProviderInfo {
+  std::string name;
+  std::vector<std::string> files;
+};
+
+/// The provider defines an interface for generating files needed for
+/// reproducing. The provider must populate its ProviderInfo to communicate
+/// its name and files to the index, before registering with the generator,
+/// i.e. in the constructor.
+///
+/// Different components will implement different providers.
+class Provider {
+public:
+  virtual ~Provider() = default;
+
+  const ProviderInfo &GetInfo() { return m_info; }
+  const FileSpec &GetDirectory() { return m_directory; }
+
+  /// The Keep method is called when it is decided that we need to keep the
+  /// data in order to provide a reproducer.
+  virtual void Keep(){};
+
+  /// The Discard method is called when it is decided that we do not need to
+  /// keep any information and will not generate a reproducer.
+  virtual void Discard(){};
+
+protected:
+  Provider(const FileSpec &directory) : m_directory(directory) {}
+
+  /// Every provider keeps track of its own files.
+  ProviderInfo m_info;
+
+private:
+  /// Every provider knows where to dump its potential files.
+  FileSpec m_directory;
+};
+
+/// The generator is responsible for the logic needed to generate a
+/// reproducer. For doing so it relies on providers, who serialize data that
+/// is necessary for reproducing  a failure.
+class Generator final {
+public:
+  Generator();
+  ~Generator();
+
+  /// Method to indicate we want to keep the reproducer. If reproducer
+  /// generation is disabled, this does nothing.
+  void Keep();
+
+  /// Method to indicate we do not want to keep the reproducer. This is
+  /// unaffected by whether or not generation reproduction is enabled, as we
+  /// might need to clean up files already written to disk.
+  void Discard();
+
+  /// Providers are registered at creating time.
+  template <typename T> T &CreateProvider() {
+    std::unique_ptr<T> provider = llvm::make_unique<T>(m_directory);
+    return static_cast<T &>(Register(std::move(provider)));
+  }
+
+  void ChangeDirectory(const FileSpec &directory);
+  const FileSpec &GetDirectory() const;
+
+private:
+  friend Reproducer;
+
+  void SetEnabled(bool enabled) { m_enabled = enabled; }
+  Provider &Register(std::unique_ptr<Provider> provider);
+  void AddProviderToIndex(const ProviderInfo &provider_info);
+
+  std::vector<std::unique_ptr<Provider>> m_providers;
+  std::mutex m_providers_mutex;
+
+  /// The reproducer root directory.
+  FileSpec m_directory;
+
+  /// Flag for controlling whether we generate a reproducer when Keep is
+  /// called.
+  bool m_enabled;
+
+  /// Flag to ensure that we never call both keep and discard.
+  bool m_done;
+};
+
+class Loader final {
+public:
+  Loader();
+
+  llvm::Optional<ProviderInfo> GetProviderInfo(llvm::StringRef name);
+  llvm::Error LoadIndex(const FileSpec &directory);
+
+  const FileSpec &GetDirectory() { return m_directory; }
+
+private:
+  llvm::StringMap<ProviderInfo> m_provider_info;
+  FileSpec m_directory;
+  bool m_loaded;
+};
+
+/// The reproducer enables clients to obtain access to the Generator and
+/// Loader.
+class Reproducer final {
+
+public:
+  static Reproducer &Instance();
+
+  Generator *GetGenerator();
+  Loader *GetLoader();
+
+  const Generator *GetGenerator() const;
+  const Loader *GetLoader() const;
+
+  llvm::Error SetGenerateReproducer(bool value);
+  llvm::Error SetReplayReproducer(bool value);
+
+  llvm::Error SetReproducerPath(const FileSpec &path);
+  FileSpec GetReproducerPath() const;
+
+private:
+  Generator m_generator;
+  Loader m_loader;
+
+  bool m_generate_reproducer = false;
+  bool m_replay_reproducer = false;
+
+  mutable std::mutex m_mutex;
+};
+
+} // namespace repro
+} // namespace lldb_private
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::repro::ProviderInfo)
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<lldb_private::repro::ProviderInfo> {
+  static void mapping(IO &io, lldb_private::repro::ProviderInfo &info) {
+    io.mapRequired("name", info.name);
+    io.mapOptional("files", info.files);
+  }
+};
+} // namespace yaml
+} // namespace llvm
+
+#endif // LLDB_UTILITY_REPRODUCER_H

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile?rev=346780&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/Makefile Tue Nov 13 11:18:16 2018
@@ -0,0 +1,5 @@
+LEVEL = ../../../make
+
+C_SOURCES := main.c
+
+include $(LEVEL)/Makefile.rules

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py?rev=346780&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/TestGdbRemoteReproducer.py Tue Nov 13 11:18:16 2018
@@ -0,0 +1,49 @@
+"""
+Test the GDB remote reproducer.
+"""
+
+from __future__ import print_function
+
+import os
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class TestGdbRemoteReproducer(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test(self):
+        """Test record and replay of gdb-remote packets."""
+        self.build()
+
+        # Create temp directory for the reproducer.
+        exe = self.getBuildArtifact("a.out")
+
+        # First capture a regular debugging session.
+        self.runCmd("reproducer capture enable")
+
+        reproducer_path = self.dbg.GetReproducerPath()
+
+        self.runCmd("file {}".format(exe))
+        self.runCmd("breakpoint set -f main.c -l 13")
+        self.runCmd("run")
+        self.runCmd("bt")
+        self.runCmd("cont")
+
+        # Generate the reproducer and stop capturing.
+        self.runCmd("reproducer generate")
+        self.runCmd("reproducer capture disable")
+
+        # Replay the session from the reproducer.
+        self.runCmd("reproducer replay {}".format(reproducer_path))
+
+        # We have to issue the same commands.
+        self.runCmd("file {}".format(exe))
+        self.runCmd("breakpoint set -f main.c -l 13")
+        self.runCmd("run")
+        self.runCmd("bt")
+        self.expect("cont")

Added: lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/main.c
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/main.c?rev=346780&view=auto
==============================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/main.c (added)
+++ lldb/trunk/packages/Python/lldbsuite/test/functionalities/reproducer/gdb-remote/main.c Tue Nov 13 11:18:16 2018
@@ -0,0 +1,19 @@
+//===-- main.c --------------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdio.h>
+
+void foo() {
+    printf("testing\n");
+}
+
+int main (int argc, char const *argv[]) {
+    foo();
+    return 0;
+}

Modified: lldb/trunk/scripts/interface/SBDebugger.i
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/scripts/interface/SBDebugger.i?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/scripts/interface/SBDebugger.i (original)
+++ lldb/trunk/scripts/interface/SBDebugger.i Tue Nov 13 11:18:16 2018
@@ -28,7 +28,7 @@ def disassemble_instructions (insts):
 # Create a new debugger instance
 debugger = lldb.SBDebugger.Create()
 
-# When we step or continue, don't return from the function until the process 
+# When we step or continue, don't return from the function until the process
 # stops. We do this by setting the async mode to false.
 debugger.SetAsync (False)
 
@@ -46,7 +46,7 @@ if target:
     # Launch the process. Since we specified synchronous mode, we won't return
     # from this function until we hit the breakpoint at main
     process = target.LaunchSimple (None, None, os.getcwd())
-    
+
     # Make sure the launch went ok
     if process:
         # Print some simple process info
@@ -122,10 +122,10 @@ public:
 
     static void
     Initialize();
-    
+
     static void
     Terminate();
-    
+
     static lldb::SBDebugger
     Create();
 
@@ -155,8 +155,8 @@ public:
 
     void
     SetAsync (bool b);
-    
-    bool 
+
+    bool
     GetAsync ();
 
     void
@@ -248,7 +248,7 @@ public:
 
     lldb::SBPlatform
     GetSelectedPlatform();
-    
+
     void
     SetSelectedPlatform(lldb::SBPlatform &platform);
 
@@ -287,7 +287,7 @@ public:
     // SBPlatform from this class.
     lldb::SBError
     SetCurrentPlatform (const char *platform_name);
-    
+
     bool
     SetCurrentPlatformSDKRoot (const char *sysroot);
 
@@ -295,8 +295,8 @@ public:
     // an interface to the Set/Get UseExternalEditor.
     bool
     SetUseExternalEditor (bool input);
-    
-    bool 
+
+    bool
     GetUseExternalEditor ();
 
     bool
@@ -342,7 +342,7 @@ public:
 
     void
     DispatchInputEndOfFile ();
-    
+
     const char *
     GetInstanceName  ();
 
@@ -366,14 +366,20 @@ public:
 
     lldb::user_id_t
     GetID ();
-    
+
     const char *
     GetPrompt() const;
 
     void
     SetPrompt (const char *prompt);
-        
-    lldb::ScriptLanguage 
+
+    const char *
+    GetReproducerPath() const;
+
+    void
+    SetReproducerPath (const char *path);
+
+    lldb::ScriptLanguage
     GetScriptLanguage() const;
 
     void
@@ -381,31 +387,31 @@ public:
 
     bool
     GetCloseInputOnEOF () const;
-    
+
     void
     SetCloseInputOnEOF (bool b);
-    
+
     lldb::SBTypeCategory
     GetCategory (const char* category_name);
-    
+
     SBTypeCategory
     GetCategory (lldb::LanguageType lang_type);
-    
+
     lldb::SBTypeCategory
     CreateCategory (const char* category_name);
-    
+
     bool
     DeleteCategory (const char* category_name);
-    
+
     uint32_t
     GetNumCategories ();
-    
+
     lldb::SBTypeCategory
     GetCategoryAtIndex (uint32_t);
-    
+
     lldb::SBTypeCategory
     GetDefaultCategory();
-    
+
     lldb::SBTypeFormat
     GetFormatForType (lldb::SBTypeNameSpecifier);
 
@@ -425,7 +431,7 @@ public:
                            int  &num_errors,
                            bool &quit_requested,
                            bool &stopped_for_crash);
-    
+
     lldb::SBError
     RunREPL (lldb::LanguageType language, const char *repl_options);
 }; // class SBDebugger

Modified: lldb/trunk/source/API/SBDebugger.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBDebugger.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/API/SBDebugger.cpp (original)
+++ lldb/trunk/source/API/SBDebugger.cpp Tue Nov 13 11:18:16 2018
@@ -1051,6 +1051,17 @@ void SBDebugger::SetPrompt(const char *p
     m_opaque_sp->SetPrompt(llvm::StringRef::withNullAsEmpty(prompt));
 }
 
+const char *SBDebugger::GetReproducerPath() const {
+  return (m_opaque_sp
+              ? ConstString(m_opaque_sp->GetReproducerPath()).GetCString()
+              : nullptr);
+}
+
+void SBDebugger::SetReproducerPath(const char *p) {
+  if (m_opaque_sp)
+    m_opaque_sp->SetReproducerPath(llvm::StringRef::withNullAsEmpty(p));
+}
+
 ScriptLanguage SBDebugger::GetScriptLanguage() const {
   return (m_opaque_sp ? m_opaque_sp->GetScriptLanguage() : eScriptLanguageNone);
 }

Modified: lldb/trunk/source/Commands/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CMakeLists.txt?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Commands/CMakeLists.txt (original)
+++ lldb/trunk/source/Commands/CMakeLists.txt Tue Nov 13 11:18:16 2018
@@ -18,6 +18,7 @@ add_lldb_library(lldbCommands
   CommandObjectProcess.cpp
   CommandObjectQuit.cpp
   CommandObjectRegister.cpp
+  CommandObjectReproducer.cpp
   CommandObjectSettings.cpp
   CommandObjectSource.cpp
   CommandObjectStats.cpp

Added: lldb/trunk/source/Commands/CommandObjectReproducer.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectReproducer.cpp?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectReproducer.cpp (added)
+++ lldb/trunk/source/Commands/CommandObjectReproducer.cpp Tue Nov 13 11:18:16 2018
@@ -0,0 +1,206 @@
+//===-- CommandObjectReproducer.cpp -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CommandObjectReproducer.h"
+
+#include "lldb/Utility/Reproducer.h"
+
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionArgParser.h"
+#include "lldb/Interpreter/OptionGroupBoolean.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+static void AppendErrorToResult(llvm::Error e, CommandReturnObject &result) {
+  std::string error_str = llvm::toString(std::move(e));
+  result.AppendErrorWithFormat("%s", error_str.c_str());
+}
+
+class CommandObjectReproducerCaptureEnable : public CommandObjectParsed {
+public:
+  CommandObjectReproducerCaptureEnable(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "reproducer capture enable",
+                            "Enable gathering information for reproducer",
+                            nullptr) {}
+
+  ~CommandObjectReproducerCaptureEnable() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (!command.empty()) {
+      result.AppendErrorWithFormat("'%s' takes no arguments",
+                                   m_cmd_name.c_str());
+      return false;
+    }
+
+    auto &r = repro::Reproducer::Instance();
+    if (auto e = r.SetGenerateReproducer(true)) {
+      AppendErrorToResult(std::move(e), result);
+      return false;
+    }
+
+    result.SetStatus(eReturnStatusSuccessFinishNoResult);
+    return result.Succeeded();
+  }
+};
+
+class CommandObjectReproducerCaptureDisable : public CommandObjectParsed {
+public:
+  CommandObjectReproducerCaptureDisable(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "reproducer capture enable",
+                            "Disable gathering information for reproducer",
+                            nullptr) {}
+
+  ~CommandObjectReproducerCaptureDisable() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (!command.empty()) {
+      result.AppendErrorWithFormat("'%s' takes no arguments",
+                                   m_cmd_name.c_str());
+      return false;
+    }
+
+    auto &r = repro::Reproducer::Instance();
+    if (auto e = r.SetGenerateReproducer(false)) {
+      AppendErrorToResult(std::move(e), result);
+      return false;
+    }
+
+    result.SetStatus(eReturnStatusSuccessFinishNoResult);
+    return result.Succeeded();
+  }
+};
+
+class CommandObjectReproducerGenerate : public CommandObjectParsed {
+public:
+  CommandObjectReproducerGenerate(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "reproducer generate",
+                            "Generate reproducer on disk.", nullptr) {}
+
+  ~CommandObjectReproducerGenerate() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (!command.empty()) {
+      result.AppendErrorWithFormat("'%s' takes no arguments",
+                                   m_cmd_name.c_str());
+      return false;
+    }
+
+    auto &r = repro::Reproducer::Instance();
+    if (auto generator = r.GetGenerator()) {
+      generator->Keep();
+    } else {
+      result.AppendErrorWithFormat("Unable to get the reproducer generator");
+      return false;
+    }
+
+    result.GetOutputStream()
+        << "Reproducer written to '" << r.GetReproducerPath() << "'\n";
+
+    result.SetStatus(eReturnStatusSuccessFinishResult);
+    return result.Succeeded();
+  }
+};
+
+class CommandObjectReproducerReplay : public CommandObjectParsed {
+public:
+  CommandObjectReproducerReplay(CommandInterpreter &interpreter)
+      : CommandObjectParsed(interpreter, "reproducer capture",
+                            "Enable or disable gathering of information needed "
+                            "to generate a reproducer.",
+                            nullptr) {
+    CommandArgumentEntry arg1;
+    CommandArgumentData path_arg;
+
+    // Define the first (and only) variant of this arg.
+    path_arg.arg_type = eArgTypePath;
+    path_arg.arg_repetition = eArgRepeatPlain;
+
+    // There is only one variant this argument could be; put it into the
+    // argument entry.
+    arg1.push_back(path_arg);
+
+    // Push the data for the first argument into the m_arguments vector.
+    m_arguments.push_back(arg1);
+  }
+
+  ~CommandObjectReproducerReplay() override = default;
+
+protected:
+  bool DoExecute(Args &command, CommandReturnObject &result) override {
+    if (command.empty()) {
+      result.AppendErrorWithFormat(
+          "'%s' takes a single argument: the reproducer path",
+          m_cmd_name.c_str());
+      return false;
+    }
+
+    auto &r = repro::Reproducer::Instance();
+
+    if (auto e = r.SetReplayReproducer(true)) {
+      std::string error_str = llvm::toString(std::move(e));
+      result.AppendErrorWithFormat("%s", error_str.c_str());
+      return false;
+    }
+
+    if (auto loader = r.GetLoader()) {
+      const char *repro_path = command.GetArgumentAtIndex(0);
+      if (auto e = loader->LoadIndex(FileSpec(repro_path))) {
+        std::string error_str = llvm::toString(std::move(e));
+        result.AppendErrorWithFormat("Unable to load reproducer: %s",
+                                     error_str.c_str());
+        return false;
+      }
+    } else {
+      result.AppendErrorWithFormat("Unable to get the reproducer loader");
+      return false;
+    }
+
+    result.SetStatus(eReturnStatusSuccessFinishNoResult);
+    return result.Succeeded();
+  }
+};
+
+class CommandObjectReproducerCapture : public CommandObjectMultiword {
+private:
+public:
+  CommandObjectReproducerCapture(CommandInterpreter &interpreter)
+      : CommandObjectMultiword(
+            interpreter, "reproducer capture",
+            "Manage gathering of information needed to generate a reproducer.",
+            NULL) {
+    LoadSubCommand(
+        "enable",
+        CommandObjectSP(new CommandObjectReproducerCaptureEnable(interpreter)));
+    LoadSubCommand("disable",
+                   CommandObjectSP(
+                       new CommandObjectReproducerCaptureDisable(interpreter)));
+  }
+
+  ~CommandObjectReproducerCapture() {}
+};
+
+CommandObjectReproducer::CommandObjectReproducer(
+    CommandInterpreter &interpreter)
+    : CommandObjectMultiword(interpreter, "reproducer",
+                             "Commands controlling LLDB reproducers.",
+                             "log <subcommand> [<command-options>]") {
+  LoadSubCommand("capture", CommandObjectSP(new CommandObjectReproducerCapture(
+                                interpreter)));
+  LoadSubCommand(
+      "generate",
+      CommandObjectSP(new CommandObjectReproducerGenerate(interpreter)));
+  LoadSubCommand("replay", CommandObjectSP(
+                               new CommandObjectReproducerReplay(interpreter)));
+}
+
+CommandObjectReproducer::~CommandObjectReproducer() = default;

Added: lldb/trunk/source/Commands/CommandObjectReproducer.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectReproducer.h?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Commands/CommandObjectReproducer.h (added)
+++ lldb/trunk/source/Commands/CommandObjectReproducer.h Tue Nov 13 11:18:16 2018
@@ -0,0 +1,31 @@
+//===-- CommandObjectReproducer.h -------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_CommandObjectReproducer_h_
+#define liblldb_CommandObjectReproducer_h_
+
+#include "lldb/Interpreter/CommandObjectMultiword.h"
+#include "lldb/Interpreter/Options.h"
+
+namespace lldb_private {
+
+//-------------------------------------------------------------------------
+// CommandObjectReproducer
+//-------------------------------------------------------------------------
+
+class CommandObjectReproducer : public CommandObjectMultiword {
+public:
+  CommandObjectReproducer(CommandInterpreter &interpreter);
+
+  ~CommandObjectReproducer() override;
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_CommandObjectReproducer_h_

Modified: lldb/trunk/source/Core/Debugger.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/Debugger.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Core/Debugger.cpp (original)
+++ lldb/trunk/source/Core/Debugger.cpp Tue Nov 13 11:18:16 2018
@@ -44,6 +44,7 @@
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Utility/AnsiTerminal.h"
 #include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
 #include "lldb/Utility/StreamCallback.h"
@@ -412,6 +413,17 @@ void Debugger::SetPrompt(llvm::StringRef
   GetCommandInterpreter().UpdatePrompt(new_prompt);
 }
 
+llvm::StringRef Debugger::GetReproducerPath() const {
+  auto &r = repro::Reproducer::Instance();
+  return r.GetReproducerPath().GetCString();
+}
+
+void Debugger::SetReproducerPath(llvm::StringRef p) {
+  auto &r = repro::Reproducer::Instance();
+  if (auto e = r.SetReproducerPath(FileSpec(p)))
+    llvm::consumeError(std::move(e));
+}
+
 const FormatEntity::Entry *Debugger::GetThreadFormat() const {
   const uint32_t idx = ePropertyThreadFormat;
   return m_collection_sp->GetPropertyAtIndexAsFormatEntity(nullptr, idx);

Modified: lldb/trunk/source/Host/common/HostInfoBase.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Host/common/HostInfoBase.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Host/common/HostInfoBase.cpp (original)
+++ lldb/trunk/source/Host/common/HostInfoBase.cpp Tue Nov 13 11:18:16 2018
@@ -194,6 +194,19 @@ FileSpec HostInfoBase::GetGlobalTempDir(
   return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
 }
 
+FileSpec HostInfoBase::GetReproducerTempDir() {
+  static llvm::once_flag g_once_flag;
+  static bool success = false;
+  llvm::call_once(g_once_flag, []() {
+    success = HostInfo::ComputeReproducerTempFileDirectory(
+        g_fields->m_lldb_global_tmp_dir);
+    Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
+    LLDB_LOG(log, "reproducer temp dir -> `{0}`",
+             g_fields->m_lldb_global_tmp_dir);
+  });
+  return success ? g_fields->m_lldb_global_tmp_dir : FileSpec();
+}
+
 ArchSpec HostInfoBase::GetAugmentedArchSpec(llvm::StringRef triple) {
   if (triple.empty())
     return ArchSpec();
@@ -272,6 +285,26 @@ bool HostInfoBase::ComputeGlobalTempFile
   if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
     return false;
 
+  file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
+  return true;
+}
+
+bool HostInfoBase::ComputeReproducerTempFileDirectory(FileSpec &file_spec) {
+  file_spec.Clear();
+
+  FileSpec temp_file_spec;
+  if (!HostInfo::ComputeTempFileBaseDirectory(temp_file_spec))
+    return false;
+
+  temp_file_spec.AppendPathComponent("reproducer");
+  if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+    return false;
+
+  std::string pid_str{llvm::to_string(Host::GetCurrentProcessID())};
+  temp_file_spec.AppendPathComponent(pid_str);
+  if (llvm::sys::fs::create_directory(temp_file_spec.GetPath()))
+    return false;
+
   file_spec.GetDirectory().SetCString(temp_file_spec.GetCString());
   return true;
 }

Modified: lldb/trunk/source/Interpreter/CommandInterpreter.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Interpreter/CommandInterpreter.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Interpreter/CommandInterpreter.cpp (original)
+++ lldb/trunk/source/Interpreter/CommandInterpreter.cpp Tue Nov 13 11:18:16 2018
@@ -31,6 +31,7 @@
 #include "Commands/CommandObjectProcess.h"
 #include "Commands/CommandObjectQuit.h"
 #include "Commands/CommandObjectRegister.h"
+#include "Commands/CommandObjectReproducer.h"
 #include "Commands/CommandObjectSettings.h"
 #include "Commands/CommandObjectSource.h"
 #include "Commands/CommandObjectStats.h"
@@ -483,6 +484,8 @@ void CommandInterpreter::LoadCommandDict
   m_command_dict["quit"] = CommandObjectSP(new CommandObjectQuit(*this));
   m_command_dict["register"] =
       CommandObjectSP(new CommandObjectRegister(*this));
+  m_command_dict["reproducer"] =
+      CommandObjectSP(new CommandObjectReproducer(*this));
   m_command_dict["script"] =
       CommandObjectSP(new CommandObjectScript(*this, script_language));
   m_command_dict["settings"] =
@@ -2387,7 +2390,7 @@ void CommandInterpreter::HandleCommandsF
         flags |= eHandleCommandFlagStopOnError;
       }
 
-      // stop-on-crash can only be set, if it is present in all levels of 
+      // stop-on-crash can only be set, if it is present in all levels of
       // pushed flag sets.
       if (options.GetStopOnCrash()) {
         if (m_command_source_flags.empty()) {

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/CMakeLists.txt?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/CMakeLists.txt (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/CMakeLists.txt Tue Nov 13 11:18:16 2018
@@ -15,6 +15,8 @@ add_lldb_library(lldbPluginProcessGDBRem
   GDBRemoteClientBase.cpp
   GDBRemoteCommunication.cpp
   GDBRemoteCommunicationClient.cpp
+  GDBRemoteCommunicationHistory.cpp
+  GDBRemoteCommunicationReplayServer.cpp
   GDBRemoteCommunicationServer.cpp
   GDBRemoteCommunicationServerCommon.cpp
   GDBRemoteCommunicationServerLLGS.cpp

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp Tue Nov 13 11:18:16 2018
@@ -9,6 +9,7 @@
 
 #include "GDBRemoteCommunication.h"
 
+#include <future>
 #include <limits.h>
 #include <string.h>
 #include <sys/stat.h>
@@ -22,6 +23,8 @@
 #include "lldb/Host/Socket.h"
 #include "lldb/Host/StringConvert.h"
 #include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Host/common/TCPSocket.h"
+#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
 #include "lldb/Target/Platform.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Utility/FileSpec.h"
@@ -51,78 +54,6 @@ using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
 
-GDBRemoteCommunication::History::History(uint32_t size)
-    : m_packets(), m_curr_idx(0), m_total_packet_count(0),
-      m_dumped_to_log(false) {
-  m_packets.resize(size);
-}
-
-GDBRemoteCommunication::History::~History() {}
-
-void GDBRemoteCommunication::History::AddPacket(char packet_char,
-                                                PacketType type,
-                                                uint32_t bytes_transmitted) {
-  const size_t size = m_packets.size();
-  if (size > 0) {
-    const uint32_t idx = GetNextIndex();
-    m_packets[idx].packet.assign(1, packet_char);
-    m_packets[idx].type = type;
-    m_packets[idx].bytes_transmitted = bytes_transmitted;
-    m_packets[idx].packet_idx = m_total_packet_count;
-    m_packets[idx].tid = llvm::get_threadid();
-  }
-}
-
-void GDBRemoteCommunication::History::AddPacket(const std::string &src,
-                                                uint32_t src_len,
-                                                PacketType type,
-                                                uint32_t bytes_transmitted) {
-  const size_t size = m_packets.size();
-  if (size > 0) {
-    const uint32_t idx = GetNextIndex();
-    m_packets[idx].packet.assign(src, 0, src_len);
-    m_packets[idx].type = type;
-    m_packets[idx].bytes_transmitted = bytes_transmitted;
-    m_packets[idx].packet_idx = m_total_packet_count;
-    m_packets[idx].tid = llvm::get_threadid();
-  }
-}
-
-void GDBRemoteCommunication::History::Dump(Stream &strm) const {
-  const uint32_t size = GetNumPacketsInHistory();
-  const uint32_t first_idx = GetFirstSavedPacketIndex();
-  const uint32_t stop_idx = m_curr_idx + size;
-  for (uint32_t i = first_idx; i < stop_idx; ++i) {
-    const uint32_t idx = NormalizeIndex(i);
-    const Entry &entry = m_packets[idx];
-    if (entry.type == ePacketTypeInvalid || entry.packet.empty())
-      break;
-    strm.Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
-                entry.packet_idx, entry.tid, entry.bytes_transmitted,
-                (entry.type == ePacketTypeSend) ? "send" : "read",
-                entry.packet.c_str());
-  }
-}
-
-void GDBRemoteCommunication::History::Dump(Log *log) const {
-  if (log && !m_dumped_to_log) {
-    m_dumped_to_log = true;
-    const uint32_t size = GetNumPacketsInHistory();
-    const uint32_t first_idx = GetFirstSavedPacketIndex();
-    const uint32_t stop_idx = m_curr_idx + size;
-    for (uint32_t i = first_idx; i < stop_idx; ++i) {
-      const uint32_t idx = NormalizeIndex(i);
-      const Entry &entry = m_packets[idx];
-      if (entry.type == ePacketTypeInvalid || entry.packet.empty())
-        break;
-      log->Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
-                  entry.packet_idx, entry.tid, entry.bytes_transmitted,
-                  (entry.type == ePacketTypeSend) ? "send" : "read",
-                  entry.packet.c_str());
-    }
-  }
-}
-
 //----------------------------------------------------------------------
 // GDBRemoteCommunication constructor
 //----------------------------------------------------------------------
@@ -169,7 +100,8 @@ size_t GDBRemoteCommunication::SendAck()
   const size_t bytes_written = Write(&ch, 1, status, NULL);
   if (log)
     log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
-  m_history.AddPacket(ch, History::ePacketTypeSend, bytes_written);
+  m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+                      bytes_written);
   return bytes_written;
 }
 
@@ -180,26 +112,31 @@ size_t GDBRemoteCommunication::SendNack(
   const size_t bytes_written = Write(&ch, 1, status, NULL);
   if (log)
     log->Printf("<%4" PRIu64 "> send packet: %c", (uint64_t)bytes_written, ch);
-  m_history.AddPacket(ch, History::ePacketTypeSend, bytes_written);
+  m_history.AddPacket(ch, GDBRemoteCommunicationHistory::ePacketTypeSend,
+                      bytes_written);
   return bytes_written;
 }
 
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunication::SendPacketNoLock(llvm::StringRef payload) {
-  if (IsConnected()) {
     StreamString packet(0, 4, eByteOrderBig);
-
     packet.PutChar('$');
     packet.Write(payload.data(), payload.size());
     packet.PutChar('#');
     packet.PutHex8(CalculcateChecksum(payload));
+    std::string packet_str = packet.GetString();
+
+    return SendRawPacketNoLock(packet_str);
+}
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunication::SendRawPacketNoLock(llvm::StringRef packet,
+                                            bool skip_ack) {
+  if (IsConnected()) {
     Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PACKETS));
     ConnectionStatus status = eConnectionStatusSuccess;
-    // TODO: Don't shimmy through a std::string, just use StringRef.
-    std::string packet_str = packet.GetString();
-    const char *packet_data = packet_str.c_str();
-    const size_t packet_length = packet.GetSize();
+    const char *packet_data = packet.data();
+    const size_t packet_length = packet.size();
     size_t bytes_written = Write(packet_data, packet_length, status, NULL);
     if (log) {
       size_t binary_start_offset = 0;
@@ -238,11 +175,12 @@ GDBRemoteCommunication::SendPacketNoLock
                     (int)packet_length, packet_data);
     }
 
-    m_history.AddPacket(packet.GetString(), packet_length,
-                        History::ePacketTypeSend, bytes_written);
+    m_history.AddPacket(packet.str(), packet_length,
+                        GDBRemoteCommunicationHistory::ePacketTypeSend,
+                        bytes_written);
 
     if (bytes_written == packet_length) {
-      if (GetSendAcks())
+      if (!skip_ack && GetSendAcks())
         return GetAck();
       else
         return PacketResult::Success;
@@ -857,7 +795,8 @@ GDBRemoteCommunication::CheckForPacket(c
         }
       }
 
-      m_history.AddPacket(m_bytes, total_length, History::ePacketTypeRecv,
+      m_history.AddPacket(m_bytes, total_length,
+                          GDBRemoteCommunicationHistory::ePacketTypeRecv,
                           total_length);
 
       // Clear packet_str in case there is some existing data in it.
@@ -1302,6 +1241,42 @@ Status GDBRemoteCommunication::StartDebu
 
 void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); }
 
+void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) {
+  m_history.SetStream(strm);
+};
+
+llvm::Error
+GDBRemoteCommunication::ConnectLocally(GDBRemoteCommunication &client,
+                                       GDBRemoteCommunication &server) {
+  const bool child_processes_inherit = false;
+  const int backlog = 5;
+  TCPSocket listen_socket(true, child_processes_inherit);
+  if (llvm::Error error =
+          listen_socket.Listen("127.0.0.1:0", backlog).ToError())
+    return error;
+
+  Socket *accept_socket;
+  std::future<Status> accept_status = std::async(
+      std::launch::async, [&] { return listen_socket.Accept(accept_socket); });
+
+  llvm::SmallString<32> remote_addr;
+  llvm::raw_svector_ostream(remote_addr)
+      << "connect://localhost:" << listen_socket.GetLocalPortNumber();
+
+  std::unique_ptr<ConnectionFileDescriptor> conn_up(
+      new ConnectionFileDescriptor());
+  if (conn_up->Connect(remote_addr, nullptr) != lldb::eConnectionStatusSuccess)
+    return llvm::make_error<llvm::StringError>("Unable to connect",
+                                               llvm::inconvertibleErrorCode());
+
+  client.SetConnection(conn_up.release());
+  if (llvm::Error error = accept_status.get().ToError())
+    return error;
+
+  server.SetConnection(new ConnectionFileDescriptor(accept_socket));
+  return llvm::Error::success();
+}
+
 GDBRemoteCommunication::ScopedTimeout::ScopedTimeout(
     GDBRemoteCommunication &gdb_comm, std::chrono::seconds timeout)
   : m_gdb_comm(gdb_comm), m_timeout_modified(false) {

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h Tue Nov 13 11:18:16 2018
@@ -10,6 +10,8 @@
 #ifndef liblldb_GDBRemoteCommunication_h_
 #define liblldb_GDBRemoteCommunication_h_
 
+#include "GDBRemoteCommunicationHistory.h"
+
 #include <condition_variable>
 #include <mutex>
 #include <queue>
@@ -22,7 +24,6 @@
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/Predicate.h"
 #include "lldb/lldb-public.h"
-
 #include "lldb/Utility/StringExtractorGDBRemote.h"
 
 namespace lldb_private {
@@ -136,86 +137,16 @@ public:
                          // fork/exec to avoid having to connect/accept
 
   void DumpHistory(Stream &strm);
+  void SetHistoryStream(llvm::raw_ostream *strm);
 
-protected:
-  class History {
-  public:
-    enum PacketType {
-      ePacketTypeInvalid = 0,
-      ePacketTypeSend,
-      ePacketTypeRecv
-    };
-
-    struct Entry {
-      Entry()
-          : packet(), type(ePacketTypeInvalid), bytes_transmitted(0),
-            packet_idx(0), tid(LLDB_INVALID_THREAD_ID) {}
-
-      void Clear() {
-        packet.clear();
-        type = ePacketTypeInvalid;
-        bytes_transmitted = 0;
-        packet_idx = 0;
-        tid = LLDB_INVALID_THREAD_ID;
-      }
-      std::string packet;
-      PacketType type;
-      uint32_t bytes_transmitted;
-      uint32_t packet_idx;
-      lldb::tid_t tid;
-    };
-
-    History(uint32_t size);
-
-    ~History();
-
-    // For single char packets for ack, nack and /x03
-    void AddPacket(char packet_char, PacketType type,
-                   uint32_t bytes_transmitted);
-
-    void AddPacket(const std::string &src, uint32_t src_len, PacketType type,
-                   uint32_t bytes_transmitted);
-
-    void Dump(Stream &strm) const;
-
-    void Dump(Log *log) const;
-
-    bool DidDumpToLog() const { return m_dumped_to_log; }
-
-  protected:
-    uint32_t GetFirstSavedPacketIndex() const {
-      if (m_total_packet_count < m_packets.size())
-        return 0;
-      else
-        return m_curr_idx + 1;
-    }
-
-    uint32_t GetNumPacketsInHistory() const {
-      if (m_total_packet_count < m_packets.size())
-        return m_total_packet_count;
-      else
-        return (uint32_t)m_packets.size();
-    }
-
-    uint32_t GetNextIndex() {
-      ++m_total_packet_count;
-      const uint32_t idx = m_curr_idx;
-      m_curr_idx = NormalizeIndex(idx + 1);
-      return idx;
-    }
-
-    uint32_t NormalizeIndex(uint32_t i) const { return i % m_packets.size(); }
-
-    std::vector<Entry> m_packets;
-    uint32_t m_curr_idx;
-    uint32_t m_total_packet_count;
-    mutable bool m_dumped_to_log;
-  };
+  static llvm::Error ConnectLocally(GDBRemoteCommunication &client,
+                                    GDBRemoteCommunication &server);
 
+protected:
   std::chrono::seconds m_packet_timeout;
   uint32_t m_echo_number;
   LazyBool m_supports_qEcho;
-  History m_history;
+  GDBRemoteCommunicationHistory m_history;
   bool m_send_acks;
   bool m_is_platform; // Set to true if this class represents a platform,
                       // false if this class represents a debug session for
@@ -224,6 +155,8 @@ protected:
   CompressionType m_compression_type;
 
   PacketResult SendPacketNoLock(llvm::StringRef payload);
+  PacketResult SendRawPacketNoLock(llvm::StringRef payload,
+                                   bool skip_ack = false);
 
   PacketResult ReadPacket(StringExtractorGDBRemote &response,
                           Timeout<std::micro> timeout, bool sync_on_timeout);

Added: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp (added)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp Tue Nov 13 11:18:16 2018
@@ -0,0 +1,143 @@
+//===-- GDBRemoteCommunicationHistory.cpp -----------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "GDBRemoteCommunicationHistory.h"
+
+// Other libraries and framework includes
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/Log.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+void GDBRemoteCommunicationHistory::Entry::Serialize(raw_ostream &strm) const {
+  yaml::Output yout(strm);
+  yout << const_cast<GDBRemoteCommunicationHistory::Entry &>(*this);
+  strm.flush();
+}
+
+GDBRemoteCommunicationHistory::GDBRemoteCommunicationHistory(uint32_t size)
+    : m_packets(), m_curr_idx(0), m_total_packet_count(0),
+      m_dumped_to_log(false) {
+  if (size)
+    m_packets.resize(size);
+}
+
+GDBRemoteCommunicationHistory::~GDBRemoteCommunicationHistory() {}
+
+void GDBRemoteCommunicationHistory::AddPacket(char packet_char, PacketType type,
+                                              uint32_t bytes_transmitted) {
+  const size_t size = m_packets.size();
+  if (size == 0)
+    return;
+
+  const uint32_t idx = GetNextIndex();
+  m_packets[idx].packet.data.assign(1, packet_char);
+  m_packets[idx].type = type;
+  m_packets[idx].bytes_transmitted = bytes_transmitted;
+  m_packets[idx].packet_idx = m_total_packet_count;
+  m_packets[idx].tid = llvm::get_threadid();
+  if (m_stream && type == ePacketTypeRecv)
+    m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::AddPacket(const std::string &src,
+                                              uint32_t src_len, PacketType type,
+                                              uint32_t bytes_transmitted) {
+  const size_t size = m_packets.size();
+  if (size == 0)
+    return;
+
+  const uint32_t idx = GetNextIndex();
+  m_packets[idx].packet.data.assign(src, 0, src_len);
+  m_packets[idx].type = type;
+  m_packets[idx].bytes_transmitted = bytes_transmitted;
+  m_packets[idx].packet_idx = m_total_packet_count;
+  m_packets[idx].tid = llvm::get_threadid();
+  if (m_stream && type == ePacketTypeRecv)
+    m_packets[idx].Serialize(*m_stream);
+}
+
+void GDBRemoteCommunicationHistory::Dump(Stream &strm) const {
+  const uint32_t size = GetNumPacketsInHistory();
+  const uint32_t first_idx = GetFirstSavedPacketIndex();
+  const uint32_t stop_idx = m_curr_idx + size;
+  for (uint32_t i = first_idx; i < stop_idx; ++i) {
+    const uint32_t idx = NormalizeIndex(i);
+    const Entry &entry = m_packets[idx];
+    if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+      break;
+    strm.Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s\n",
+                entry.packet_idx, entry.tid, entry.bytes_transmitted,
+                (entry.type == ePacketTypeSend) ? "send" : "read",
+                entry.packet.data.c_str());
+  }
+}
+
+void GDBRemoteCommunicationHistory::Dump(Log *log) const {
+  if (!log || m_dumped_to_log)
+    return;
+
+  m_dumped_to_log = true;
+  const uint32_t size = GetNumPacketsInHistory();
+  const uint32_t first_idx = GetFirstSavedPacketIndex();
+  const uint32_t stop_idx = m_curr_idx + size;
+  for (uint32_t i = first_idx; i < stop_idx; ++i) {
+    const uint32_t idx = NormalizeIndex(i);
+    const Entry &entry = m_packets[idx];
+    if (entry.type == ePacketTypeInvalid || entry.packet.data.empty())
+      break;
+    log->Printf("history[%u] tid=0x%4.4" PRIx64 " <%4u> %s packet: %s",
+                entry.packet_idx, entry.tid, entry.bytes_transmitted,
+                (entry.type == ePacketTypeSend) ? "send" : "read",
+                entry.packet.data.c_str());
+  }
+}
+
+void yaml::ScalarEnumerationTraits<GDBRemoteCommunicationHistory::PacketType>::
+    enumeration(IO &io, GDBRemoteCommunicationHistory::PacketType &value) {
+  io.enumCase(value, "Invalid",
+              GDBRemoteCommunicationHistory::ePacketTypeInvalid);
+  io.enumCase(value, "Send", GDBRemoteCommunicationHistory::ePacketTypeSend);
+  io.enumCase(value, "Recv", GDBRemoteCommunicationHistory::ePacketTypeRecv);
+}
+
+void yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::
+    output(const GDBRemoteCommunicationHistory::Entry::BinaryData &Val, void *,
+           raw_ostream &Out) {
+  Out << toHex(Val.data);
+}
+
+StringRef
+yaml::ScalarTraits<GDBRemoteCommunicationHistory::Entry::BinaryData>::input(
+    StringRef Scalar, void *,
+    GDBRemoteCommunicationHistory::Entry::BinaryData &Val) {
+  Val.data = fromHex(Scalar);
+  return {};
+}
+
+void yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::mapping(
+    IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+  io.mapRequired("packet", Entry.packet);
+  io.mapRequired("type", Entry.type);
+  io.mapRequired("bytes", Entry.bytes_transmitted);
+  io.mapRequired("index", Entry.packet_idx);
+  io.mapRequired("tid", Entry.tid);
+}
+
+StringRef yaml::MappingTraits<GDBRemoteCommunicationHistory::Entry>::validate(
+    IO &io, GDBRemoteCommunicationHistory::Entry &Entry) {
+  if (Entry.bytes_transmitted != Entry.packet.data.size())
+    return "BinaryData size doesn't match bytes transmitted";
+
+  return {};
+}

Added: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h (added)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h Tue Nov 13 11:18:16 2018
@@ -0,0 +1,156 @@
+//===-- GDBRemoteCommunicationHistory.h--------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationHistory_h_
+#define liblldb_GDBRemoteCommunicationHistory_h_
+
+#include <string>
+#include <vector>
+
+#include "lldb/lldb-public.h"
+#include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+/// The history keeps a circular buffer of GDB remote packets. The history is
+/// used for logging and replaying GDB remote packets.
+class GDBRemoteCommunicationHistory {
+public:
+  friend llvm::yaml::MappingTraits<GDBRemoteCommunicationHistory>;
+
+  enum PacketType { ePacketTypeInvalid = 0, ePacketTypeSend, ePacketTypeRecv };
+
+  /// Entry in the ring buffer containing the packet data, its type, size and
+  /// index. Entries can be serialized to file.
+  struct Entry {
+    Entry()
+        : packet(), type(ePacketTypeInvalid), bytes_transmitted(0),
+          packet_idx(0), tid(LLDB_INVALID_THREAD_ID) {}
+
+    void Clear() {
+      packet.data.clear();
+      type = ePacketTypeInvalid;
+      bytes_transmitted = 0;
+      packet_idx = 0;
+      tid = LLDB_INVALID_THREAD_ID;
+    }
+
+    struct BinaryData {
+      std::string data;
+    };
+
+    void Serialize(llvm::raw_ostream &strm) const;
+
+    BinaryData packet;
+    PacketType type;
+    uint32_t bytes_transmitted;
+    uint32_t packet_idx;
+    lldb::tid_t tid;
+  };
+
+  GDBRemoteCommunicationHistory(uint32_t size = 0);
+
+  ~GDBRemoteCommunicationHistory();
+
+  // For single char packets for ack, nack and /x03
+  void AddPacket(char packet_char, PacketType type, uint32_t bytes_transmitted);
+
+  void AddPacket(const std::string &src, uint32_t src_len, PacketType type,
+                 uint32_t bytes_transmitted);
+
+  void Dump(Stream &strm) const;
+  void Dump(Log *log) const;
+  bool DidDumpToLog() const { return m_dumped_to_log; }
+
+  void SetStream(llvm::raw_ostream *strm) { m_stream = strm; }
+
+private:
+  uint32_t GetFirstSavedPacketIndex() const {
+    if (m_total_packet_count < m_packets.size())
+      return 0;
+    else
+      return m_curr_idx + 1;
+  }
+
+  uint32_t GetNumPacketsInHistory() const {
+    if (m_total_packet_count < m_packets.size())
+      return m_total_packet_count;
+    else
+      return (uint32_t)m_packets.size();
+  }
+
+  uint32_t GetNextIndex() {
+    ++m_total_packet_count;
+    const uint32_t idx = m_curr_idx;
+    m_curr_idx = NormalizeIndex(idx + 1);
+    return idx;
+  }
+
+  uint32_t NormalizeIndex(uint32_t i) const {
+    return m_packets.empty() ? 0 : i % m_packets.size();
+  }
+
+  std::vector<Entry> m_packets;
+  uint32_t m_curr_idx;
+  uint32_t m_total_packet_count;
+  mutable bool m_dumped_to_log;
+  llvm::raw_ostream *m_stream = nullptr;
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+    lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry)
+
+namespace llvm {
+namespace yaml {
+
+template <>
+struct ScalarEnumerationTraits<lldb_private::process_gdb_remote::
+                                   GDBRemoteCommunicationHistory::PacketType> {
+  static void enumeration(IO &io,
+                          lldb_private::process_gdb_remote::
+                              GDBRemoteCommunicationHistory::PacketType &value);
+};
+
+template <>
+struct ScalarTraits<lldb_private::process_gdb_remote::
+                        GDBRemoteCommunicationHistory::Entry::BinaryData> {
+  static void output(const lldb_private::process_gdb_remote::
+                         GDBRemoteCommunicationHistory::Entry::BinaryData &,
+                     void *, raw_ostream &);
+
+  static StringRef
+  input(StringRef, void *,
+        lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry::
+            BinaryData &);
+
+  static QuotingType mustQuote(StringRef S) { return QuotingType::None; }
+};
+
+template <>
+struct MappingTraits<
+    lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry> {
+  static void
+  mapping(IO &io,
+          lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry
+              &Entry);
+
+  static StringRef validate(
+      IO &io,
+      lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry &);
+};
+
+} // namespace yaml
+} // namespace llvm
+
+#endif // liblldb_GDBRemoteCommunicationHistory_h_

Added: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp (added)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.cpp Tue Nov 13 11:18:16 2018
@@ -0,0 +1,204 @@
+//===-- GDBRemoteCommunicationReplayServer.cpp ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <errno.h>
+
+#include "lldb/Host/Config.h"
+
+#include "GDBRemoteCommunicationReplayServer.h"
+#include "ProcessGDBRemoteLog.h"
+
+// C Includes
+// C++ Includes
+#include <cstring>
+
+// Project includes
+#include "lldb/Core/Event.h"
+#include "lldb/Host/ThreadLauncher.h"
+#include "lldb/Utility/ConstString.h"
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/StreamString.h"
+#include "lldb/Utility/StringExtractorGDBRemote.h"
+
+using namespace llvm;
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+GDBRemoteCommunicationReplayServer::GDBRemoteCommunicationReplayServer()
+    : GDBRemoteCommunication("gdb-remote.server",
+                             "gdb-remote.server.rx_packet"),
+      m_async_broadcaster(nullptr, "lldb.gdb-remote.server.async-broadcaster"),
+      m_async_listener_sp(
+          Listener::MakeListener("lldb.gdb-remote.server.async-listener")),
+      m_async_thread_state_mutex(), m_skip_acks(false) {
+  m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
+                                   "async thread continue");
+  m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
+                                   "async thread should exit");
+
+  const uint32_t async_event_mask =
+      eBroadcastBitAsyncContinue | eBroadcastBitAsyncThreadShouldExit;
+  m_async_listener_sp->StartListeningForEvents(&m_async_broadcaster,
+                                               async_event_mask);
+}
+
+GDBRemoteCommunicationReplayServer::~GDBRemoteCommunicationReplayServer() {
+  StopAsyncThread();
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationReplayServer::GetPacketAndSendResponse(
+    Timeout<std::micro> timeout, Status &error, bool &interrupt, bool &quit) {
+  StringExtractorGDBRemote packet;
+  PacketResult packet_result = WaitForPacketNoLock(packet, timeout, false);
+
+  if (packet_result != PacketResult::Success) {
+    if (!IsConnected()) {
+      error.SetErrorString("lost connection");
+      quit = true;
+    } else {
+      error.SetErrorString("timeout");
+    }
+    return packet_result;
+  }
+
+  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+  if (m_skip_acks) {
+    const StringExtractorGDBRemote::ServerPacketType packet_type =
+        packet.GetServerPacketType();
+    switch (packet_type) {
+    case StringExtractorGDBRemote::eServerPacketType_nack:
+    case StringExtractorGDBRemote::eServerPacketType_ack:
+      return PacketResult::Success;
+    default:
+      break;
+    }
+  } else if (packet.GetStringRef() == "QStartNoAckMode") {
+    m_skip_acks = true;
+    m_send_acks = false;
+  }
+
+  while (!m_packet_history.empty()) {
+    // Pop last packet from the history.
+    GDBRemoteCommunicationHistory::Entry entry = m_packet_history.back();
+    m_packet_history.pop_back();
+
+    // We only care about what we received from the server. Skip everything
+    // the client sent.
+    if (entry.type != GDBRemoteCommunicationHistory::ePacketTypeRecv)
+      continue;
+
+    return SendRawPacketNoLock(entry.packet.data, true);
+  }
+
+  quit = true;
+
+  return packet_result;
+}
+
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(
+    std::vector<
+        lldb_private::process_gdb_remote::GDBRemoteCommunicationHistory::Entry>)
+
+llvm::Error
+GDBRemoteCommunicationReplayServer::LoadReplayHistory(const FileSpec &path) {
+  auto error_or_file = MemoryBuffer::getFile(path.GetPath());
+  if (auto err = error_or_file.getError())
+    return errorCodeToError(err);
+
+  yaml::Input yin((*error_or_file)->getBuffer());
+  yin >> m_packet_history;
+
+  if (auto err = yin.error())
+    return errorCodeToError(err);
+
+  // We want to manipulate the vector like a stack so we need to reverse the
+  // order of the packets to have the oldest on at the back.
+  std::reverse(m_packet_history.begin(), m_packet_history.end());
+
+  return Error::success();
+}
+
+bool GDBRemoteCommunicationReplayServer::StartAsyncThread() {
+  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+  if (!m_async_thread.IsJoinable()) {
+    // Create a thread that watches our internal state and controls which
+    // events make it to clients (into the DCProcess event queue).
+    m_async_thread = ThreadLauncher::LaunchThread(
+        "<lldb.gdb-remote.server.async>",
+        GDBRemoteCommunicationReplayServer::AsyncThread, this, nullptr);
+  }
+
+  // Wait for handshake.
+  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+
+  return m_async_thread.IsJoinable();
+}
+
+void GDBRemoteCommunicationReplayServer::StopAsyncThread() {
+  std::lock_guard<std::recursive_mutex> guard(m_async_thread_state_mutex);
+
+  if (!m_async_thread.IsJoinable())
+    return;
+
+  // Request thread to stop.
+  m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncThreadShouldExit);
+
+  // Disconnect client.
+  Disconnect();
+
+  // Stop the thread.
+  m_async_thread.Join(nullptr);
+  m_async_thread.Reset();
+}
+
+void GDBRemoteCommunicationReplayServer::ReceivePacket(
+    GDBRemoteCommunicationReplayServer &server, bool &done) {
+  Status error;
+  bool interrupt;
+  auto packet_result = server.GetPacketAndSendResponse(std::chrono::seconds(1),
+                                                       error, interrupt, done);
+  if (packet_result != GDBRemoteCommunication::PacketResult::Success &&
+      packet_result !=
+          GDBRemoteCommunication::PacketResult::ErrorReplyTimeout) {
+    done = true;
+  } else {
+    server.m_async_broadcaster.BroadcastEvent(eBroadcastBitAsyncContinue);
+  }
+}
+
+thread_result_t GDBRemoteCommunicationReplayServer::AsyncThread(void *arg) {
+  GDBRemoteCommunicationReplayServer *server =
+      (GDBRemoteCommunicationReplayServer *)arg;
+
+  EventSP event_sp;
+  bool done = false;
+
+  while (true) {
+    if (server->m_async_listener_sp->GetEvent(event_sp, llvm::None)) {
+      const uint32_t event_type = event_sp->GetType();
+      if (event_sp->BroadcasterIs(&server->m_async_broadcaster)) {
+        switch (event_type) {
+        case eBroadcastBitAsyncContinue:
+          ReceivePacket(*server, done);
+          if (done)
+            return nullptr;
+          break;
+        case eBroadcastBitAsyncThreadShouldExit:
+        default:
+          return nullptr;
+        }
+      }
+    }
+  }
+
+  return nullptr;
+}

Added: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h (added)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationReplayServer.h Tue Nov 13 11:18:16 2018
@@ -0,0 +1,83 @@
+//===-- GDBRemoteCommunicationReplayServer.h --------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_GDBRemoteCommunicationReplayServer_h_
+#define liblldb_GDBRemoteCommunicationReplayServer_h_
+
+// Other libraries and framework includes
+#include "GDBRemoteCommunication.h"
+#include "GDBRemoteCommunicationHistory.h"
+
+// Project includes
+#include "lldb/Core/Broadcaster.h"
+#include "lldb/Host/HostThread.h"
+#include "lldb/lldb-private-forward.h"
+#include "llvm/Support/Error.h"
+
+// C Includes
+// C++ Includes
+#include <functional>
+#include <map>
+#include <thread>
+
+class StringExtractorGDBRemote;
+
+namespace lldb_private {
+namespace process_gdb_remote {
+
+class ProcessGDBRemote;
+
+/// Dummy GDB server that replays packets from the GDB Remote Communication
+/// history. This is used to replay GDB packets.
+class GDBRemoteCommunicationReplayServer : public GDBRemoteCommunication {
+public:
+  GDBRemoteCommunicationReplayServer();
+
+  ~GDBRemoteCommunicationReplayServer() override;
+
+  PacketResult GetPacketAndSendResponse(Timeout<std::micro> timeout,
+                                        Status &error, bool &interrupt,
+                                        bool &quit);
+
+  bool HandshakeWithClient() { return GetAck() == PacketResult::Success; }
+
+  llvm::Error LoadReplayHistory(const FileSpec &path);
+
+  bool StartAsyncThread();
+  void StopAsyncThread();
+
+protected:
+  enum {
+    eBroadcastBitAsyncContinue = (1 << 0),
+    eBroadcastBitAsyncThreadShouldExit = (1 << 1),
+  };
+
+  static void ReceivePacket(GDBRemoteCommunicationReplayServer &server,
+                            bool &done);
+  static lldb::thread_result_t AsyncThread(void *arg);
+
+  /// Replay history with the oldest packet at the end.
+  std::vector<GDBRemoteCommunicationHistory::Entry> m_packet_history;
+
+  /// Server thread.
+  Broadcaster m_async_broadcaster;
+  lldb::ListenerSP m_async_listener_sp;
+  HostThread m_async_thread;
+  std::recursive_mutex m_async_thread_state_mutex;
+
+  bool m_skip_acks;
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(GDBRemoteCommunicationReplayServer);
+};
+
+} // namespace process_gdb_remote
+} // namespace lldb_private
+
+#endif // liblldb_GDBRemoteCommunicationReplayServer_h_

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp Tue Nov 13 11:18:16 2018
@@ -65,6 +65,7 @@
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/CleanUp.h"
 #include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Reproducer.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/StreamString.h"
 #include "lldb/Utility/Timer.h"
@@ -85,6 +86,7 @@
 #include "llvm/Support/raw_ostream.h"
 
 #define DEBUGSERVER_BASENAME "debugserver"
+using namespace llvm;
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::process_gdb_remote;
@@ -104,7 +106,7 @@ void DumpProcessGDBRemotePacketHistory(v
   if (error.Success())
     ((ProcessGDBRemote *)p)->GetGDBRemote().DumpHistory(strm);
 }
-}
+} // namespace lldb
 
 namespace {
 
@@ -155,7 +157,37 @@ static const ProcessKDPPropertiesSP &Get
   return g_settings_sp;
 }
 
-} // anonymous namespace end
+class ProcessGDBRemoteProvider : public repro::Provider {
+public:
+  ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {
+    m_info.name = "gdb-remote";
+    m_info.files.push_back("gdb-remote.yaml");
+  }
+
+  raw_ostream *GetHistoryStream() {
+    FileSpec history_file =
+        GetDirectory().CopyByAppendingPathComponent("gdb-remote.yaml");
+
+    std::error_code EC;
+    m_stream_up = make_unique<raw_fd_ostream>(history_file.GetPath(), EC,
+                                              sys::fs::OpenFlags::F_None);
+    return m_stream_up.get();
+  }
+
+  void SetCallback(std::function<void()> callback) {
+    m_callback = std::move(callback);
+  }
+
+  void Keep() override { m_callback(); }
+
+  void Discard() override { m_callback(); }
+
+private:
+  std::function<void()> m_callback;
+  std::unique_ptr<raw_fd_ostream> m_stream_up;
+};
+
+} // namespace
 
 // TODO Randomly assigning a port is unsafe.  We should get an unused
 // ephemeral port from the kernel and make sure we reserve it before passing it
@@ -256,8 +288,8 @@ ProcessGDBRemote::ProcessGDBRemote(lldb:
       m_addr_to_mmap_size(), m_thread_create_bp_sp(),
       m_waiting_for_attach(false), m_destroy_tried_resuming(false),
       m_command_sp(), m_breakpoint_pc_offset(0),
-      m_initial_tid(LLDB_INVALID_THREAD_ID), m_allow_flash_writes(false),
-      m_erased_flash_ranges() {
+      m_initial_tid(LLDB_INVALID_THREAD_ID), m_replay_mode(false),
+      m_allow_flash_writes(false), m_erased_flash_ranges() {
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadShouldExit,
                                    "async thread should exit");
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncContinue,
@@ -265,6 +297,16 @@ ProcessGDBRemote::ProcessGDBRemote(lldb:
   m_async_broadcaster.SetEventName(eBroadcastBitAsyncThreadDidExit,
                                    "async thread did exit");
 
+  repro::Generator *generator = repro::Reproducer::Instance().GetGenerator();
+  if (generator) {
+    ProcessGDBRemoteProvider &provider =
+        generator->CreateProvider<ProcessGDBRemoteProvider>();
+    // Set the history stream to the stream owned by the provider.
+    m_gdb_comm.SetHistoryStream(provider.GetHistoryStream());
+    // Make sure to clear the stream again when we're finished.
+    provider.SetCallback([&]() { m_gdb_comm.SetHistoryStream(nullptr); });
+  }
+
   Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC));
 
   const uint32_t async_event_mask =
@@ -1057,9 +1099,10 @@ void ProcessGDBRemote::DidLaunchOrAttach
       if (log)
         log->Printf("ProcessGDBRemote::%s gdb-remote had process architecture, "
                     "using %s %s",
-                    __FUNCTION__, process_arch.GetArchitectureName()
-                                      ? process_arch.GetArchitectureName()
-                                      : "<null>",
+                    __FUNCTION__,
+                    process_arch.GetArchitectureName()
+                        ? process_arch.GetArchitectureName()
+                        : "<null>",
                     process_arch.GetTriple().getTriple().c_str()
                         ? process_arch.GetTriple().getTriple().c_str()
                         : "<null>");
@@ -1068,9 +1111,10 @@ void ProcessGDBRemote::DidLaunchOrAttach
       if (log)
         log->Printf("ProcessGDBRemote::%s gdb-remote did not have process "
                     "architecture, using gdb-remote host architecture %s %s",
-                    __FUNCTION__, process_arch.GetArchitectureName()
-                                      ? process_arch.GetArchitectureName()
-                                      : "<null>",
+                    __FUNCTION__,
+                    process_arch.GetArchitectureName()
+                        ? process_arch.GetArchitectureName()
+                        : "<null>",
                     process_arch.GetTriple().getTriple().c_str()
                         ? process_arch.GetTriple().getTriple().c_str()
                         : "<null>");
@@ -1082,9 +1126,10 @@ void ProcessGDBRemote::DidLaunchOrAttach
         if (log)
           log->Printf(
               "ProcessGDBRemote::%s analyzing target arch, currently %s %s",
-              __FUNCTION__, target_arch.GetArchitectureName()
-                                ? target_arch.GetArchitectureName()
-                                : "<null>",
+              __FUNCTION__,
+              target_arch.GetArchitectureName()
+                  ? target_arch.GetArchitectureName()
+                  : "<null>",
               target_arch.GetTriple().getTriple().c_str()
                   ? target_arch.GetTriple().getTriple().c_str()
                   : "<null>");
@@ -1104,9 +1149,10 @@ void ProcessGDBRemote::DidLaunchOrAttach
           if (log)
             log->Printf("ProcessGDBRemote::%s remote process is ARM/Apple, "
                         "setting target arch to %s %s",
-                        __FUNCTION__, process_arch.GetArchitectureName()
-                                          ? process_arch.GetArchitectureName()
-                                          : "<null>",
+                        __FUNCTION__,
+                        process_arch.GetArchitectureName()
+                            ? process_arch.GetArchitectureName()
+                            : "<null>",
                         process_arch.GetTriple().getTriple().c_str()
                             ? process_arch.GetTriple().getTriple().c_str()
                             : "<null>");
@@ -1134,9 +1180,10 @@ void ProcessGDBRemote::DidLaunchOrAttach
         if (log)
           log->Printf("ProcessGDBRemote::%s final target arch after "
                       "adjustments for remote architecture: %s %s",
-                      __FUNCTION__, target_arch.GetArchitectureName()
-                                        ? target_arch.GetArchitectureName()
-                                        : "<null>",
+                      __FUNCTION__,
+                      target_arch.GetArchitectureName()
+                          ? target_arch.GetArchitectureName()
+                          : "<null>",
                       target_arch.GetTriple().getTriple().c_str()
                           ? target_arch.GetTriple().getTriple().c_str()
                           : "<null>");
@@ -3377,6 +3424,43 @@ Status ProcessGDBRemote::DoSignal(int si
   return error;
 }
 
+Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) {
+  if (!loader)
+    return Status("No loader provided.");
+
+  auto provider_info = loader->GetProviderInfo("gdb-remote");
+  if (!provider_info)
+    return Status("No provider for gdb-remote.");
+
+  if (provider_info->files.empty())
+    return Status("Provider for  gdb-remote contains no files.");
+
+  // Construct replay history path.
+  FileSpec history_file(loader->GetDirectory());
+  history_file.AppendPathComponent(provider_info->files.front());
+
+  // Enable replay mode.
+  m_replay_mode = true;
+
+  // Load replay history.
+  if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file))
+    return Status("Unable to load replay history");
+
+  // Make a local connection.
+  if (auto error = GDBRemoteCommunication::ConnectLocally(m_gdb_comm,
+                                                          m_gdb_replay_server))
+    return Status("Unable to connect to replay server");
+
+  // Start server thread.
+  m_gdb_replay_server.StartAsyncThread();
+
+  // Start client thread.
+  StartAsyncThread();
+
+  // Do the usual setup.
+  return ConnectToDebugserver("");
+}
+
 Status
 ProcessGDBRemote::EstablishConnectionIfNeeded(const ProcessInfo &process_info) {
   // Make sure we aren't already connected?
@@ -3387,6 +3471,9 @@ ProcessGDBRemote::EstablishConnectionIfN
   if (platform_sp && !platform_sp->IsHost())
     return Status("Lost debug server connection");
 
+  if (repro::Loader *loader = repro::Reproducer::Instance().GetLoader())
+    return ConnectToReplayServer(loader);
+
   auto error = LaunchAndConnectToDebugserver(process_info);
   if (error.Fail()) {
     const char *error_string = error.AsCString();
@@ -3496,7 +3583,7 @@ bool ProcessGDBRemote::MonitorDebugserve
     bool exited,    // True if the process did exit
     int signo,      // Zero for no signal
     int exit_status // Exit value of process if signal is zero
-    ) {
+) {
   // "debugserver_pid" argument passed in is the process ID for debugserver
   // that we are tracking...
   Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
@@ -4268,8 +4355,9 @@ bool ParseRegisters(XMLNode feature_node
     return false;
 
   feature_node.ForEachChildElementWithName(
-      "reg", [&target_info, &dyn_reg_info, &cur_reg_num, &reg_offset,
-              &abi_sp](const XMLNode &reg_node) -> bool {
+      "reg",
+      [&target_info, &dyn_reg_info, &cur_reg_num, &reg_offset,
+       &abi_sp](const XMLNode &reg_node) -> bool {
         std::string gdb_group;
         std::string gdb_type;
         ConstString reg_name;
@@ -4431,7 +4519,7 @@ bool ParseRegisters(XMLNode feature_node
   return true;
 }
 
-} // namespace {}
+} // namespace
 
 // query the target of gdb-remote for extended target information return:
 // 'true'  on success

Modified: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h (original)
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h Tue Nov 13 11:18:16 2018
@@ -34,11 +34,15 @@
 #include "lldb/lldb-private-forward.h"
 
 #include "GDBRemoteCommunicationClient.h"
+#include "GDBRemoteCommunicationReplayServer.h"
 #include "GDBRemoteRegisterContext.h"
 
 #include "llvm/ADT/DenseMap.h"
 
 namespace lldb_private {
+namespace repro {
+class Loader;
+}
 namespace process_gdb_remote {
 
 class ThreadGDBRemote;
@@ -262,6 +266,7 @@ protected:
   };
 
   GDBRemoteCommunicationClient m_gdb_comm;
+  GDBRemoteCommunicationReplayServer m_gdb_replay_server;
   std::atomic<lldb::pid_t> m_debugserver_pid;
   std::vector<StringExtractorGDBRemote> m_stop_packet_stack; // The stop packet
                                                              // stack replaces
@@ -302,6 +307,7 @@ protected:
   int64_t m_breakpoint_pc_offset;
   lldb::tid_t m_initial_tid; // The initial thread ID, given by stub on attach
 
+  bool m_replay_mode;
   bool m_allow_flash_writes;
   using FlashRangeVector = lldb_private::RangeVector<lldb::addr_t, size_t>;
   using FlashRange = FlashRangeVector::Entry;
@@ -329,6 +335,8 @@ protected:
   bool UpdateThreadList(ThreadList &old_thread_list,
                         ThreadList &new_thread_list) override;
 
+  Status ConnectToReplayServer(repro::Loader *loader);
+
   Status EstablishConnectionIfNeeded(const ProcessInfo &process_info);
 
   Status LaunchAndConnectToDebugserver(const ProcessInfo &process_info);

Modified: lldb/trunk/source/Utility/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/CMakeLists.txt?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/source/Utility/CMakeLists.txt (original)
+++ lldb/trunk/source/Utility/CMakeLists.txt Tue Nov 13 11:18:16 2018
@@ -63,6 +63,7 @@ add_lldb_library(lldbUtility
   Range.cpp
   RegisterValue.cpp
   RegularExpression.cpp
+  Reproducer.cpp
   Scalar.cpp
   SelectHelper.cpp
   SharingPtr.cpp

Added: lldb/trunk/source/Utility/Reproducer.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/Reproducer.cpp?rev=346780&view=auto
==============================================================================
--- lldb/trunk/source/Utility/Reproducer.cpp (added)
+++ lldb/trunk/source/Utility/Reproducer.cpp Tue Nov 13 11:18:16 2018
@@ -0,0 +1,197 @@
+//===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Reproducer.h"
+#include "lldb/Host/HostInfo.h"
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Threading.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace lldb_private;
+using namespace lldb_private::repro;
+using namespace llvm;
+using namespace llvm::yaml;
+
+Reproducer &Reproducer::Instance() {
+  static Reproducer g_reproducer;
+  return g_reproducer;
+}
+
+const Generator *Reproducer::GetGenerator() const {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  if (m_generate_reproducer)
+    return &m_generator;
+  return nullptr;
+}
+
+const Loader *Reproducer::GetLoader() const {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  if (m_replay_reproducer)
+    return &m_loader;
+  return nullptr;
+}
+
+Generator *Reproducer::GetGenerator() {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  if (m_generate_reproducer)
+    return &m_generator;
+  return nullptr;
+}
+
+Loader *Reproducer::GetLoader() {
+  std::lock_guard<std::mutex> guard(m_mutex);
+  if (m_replay_reproducer)
+    return &m_loader;
+  return nullptr;
+}
+
+llvm::Error Reproducer::SetGenerateReproducer(bool value) {
+  std::lock_guard<std::mutex> guard(m_mutex);
+
+  if (value && m_replay_reproducer)
+    return make_error<StringError>(
+        "cannot generate a reproducer when replay one",
+        inconvertibleErrorCode());
+
+  m_generate_reproducer = value;
+  m_generator.SetEnabled(value);
+
+  return Error::success();
+}
+
+llvm::Error Reproducer::SetReplayReproducer(bool value) {
+  std::lock_guard<std::mutex> guard(m_mutex);
+
+  if (value && m_generate_reproducer)
+    return make_error<StringError>(
+        "cannot replay a reproducer when generating one",
+        inconvertibleErrorCode());
+
+  m_replay_reproducer = value;
+
+  return Error::success();
+}
+
+llvm::Error Reproducer::SetReproducerPath(const FileSpec &path) {
+  // Setting the path implies using the reproducer.
+  if (auto e = SetReplayReproducer(true))
+    return e;
+
+  // Tell the reproducer to load the index form the given path.
+  if (auto loader = GetLoader()) {
+    if (auto e = loader->LoadIndex(path))
+      return e;
+  }
+
+  return make_error<StringError>("unable to get loader",
+                                 inconvertibleErrorCode());
+}
+
+FileSpec Reproducer::GetReproducerPath() const {
+  if (auto g = GetGenerator())
+    return g->GetDirectory();
+  return {};
+}
+
+Generator::Generator() : m_enabled(false), m_done(false) {
+  m_directory = HostInfo::GetReproducerTempDir();
+}
+
+Generator::~Generator() {}
+
+Provider &Generator::Register(std::unique_ptr<Provider> provider) {
+  std::lock_guard<std::mutex> lock(m_providers_mutex);
+
+  AddProviderToIndex(provider->GetInfo());
+
+  m_providers.push_back(std::move(provider));
+  return *m_providers.back();
+}
+
+void Generator::Keep() {
+  assert(!m_done);
+  m_done = true;
+
+  if (!m_enabled)
+    return;
+
+  for (auto &provider : m_providers)
+    provider->Keep();
+}
+
+void Generator::Discard() {
+  assert(!m_done);
+  m_done = true;
+
+  if (!m_enabled)
+    return;
+
+  for (auto &provider : m_providers)
+    provider->Discard();
+
+  llvm::sys::fs::remove_directories(m_directory.GetPath());
+}
+
+void Generator::ChangeDirectory(const FileSpec &directory) {
+  assert(m_providers.empty() && "Changing the directory after providers have "
+                                "been registered would invalidate the index.");
+  m_directory = directory;
+}
+
+const FileSpec &Generator::GetDirectory() const { return m_directory; }
+
+void Generator::AddProviderToIndex(const ProviderInfo &provider_info) {
+  FileSpec index = m_directory;
+  index.AppendPathComponent("index.yaml");
+
+  std::error_code EC;
+  auto strm = make_unique<raw_fd_ostream>(index.GetPath(), EC,
+                                          sys::fs::OpenFlags::F_None);
+  yaml::Output yout(*strm);
+  yout << const_cast<ProviderInfo &>(provider_info);
+}
+
+Loader::Loader() : m_loaded(false) {}
+
+llvm::Error Loader::LoadIndex(const FileSpec &directory) {
+  if (m_loaded)
+    return llvm::Error::success();
+
+  FileSpec index = directory.CopyByAppendingPathComponent("index.yaml");
+
+  auto error_or_file = MemoryBuffer::getFile(index.GetPath());
+  if (auto err = error_or_file.getError())
+    return errorCodeToError(err);
+
+  std::vector<ProviderInfo> provider_info;
+  yaml::Input yin((*error_or_file)->getBuffer());
+  yin >> provider_info;
+
+  if (auto err = yin.error())
+    return errorCodeToError(err);
+
+  for (auto &info : provider_info)
+    m_provider_info[info.name] = info;
+
+  m_directory = directory;
+  m_loaded = true;
+
+  return llvm::Error::success();
+}
+
+llvm::Optional<ProviderInfo> Loader::GetProviderInfo(StringRef name) {
+  assert(m_loaded);
+
+  auto it = m_provider_info.find(name);
+  if (it == m_provider_info.end())
+    return llvm::None;
+
+  return it->second;
+}

Modified: lldb/trunk/tools/driver/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/tools/driver/Driver.cpp (original)
+++ lldb/trunk/tools/driver/Driver.cpp Tue Nov 13 11:18:16 2018
@@ -120,19 +120,23 @@ static constexpr OptionDefinition g_opti
      "Tells the debugger to execute this one-line lldb command after any file "
      "provided on the command line has been loaded."},
     {LLDB_3_TO_5, false, "source-before-file", 'S', required_argument, 0,
-     eArgTypeFilename, "Tells the debugger to read in and execute the lldb "
-                       "commands in the given file, before any file provided "
-                       "on the command line has been loaded."},
+     eArgTypeFilename,
+     "Tells the debugger to read in and execute the lldb "
+     "commands in the given file, before any file provided "
+     "on the command line has been loaded."},
     {LLDB_3_TO_5, false, "one-line-before-file", 'O', required_argument, 0,
-     eArgTypeNone, "Tells the debugger to execute this one-line lldb command "
-                   "before any file provided on the command line has been "
-                   "loaded."},
+     eArgTypeNone,
+     "Tells the debugger to execute this one-line lldb command "
+     "before any file provided on the command line has been "
+     "loaded."},
     {LLDB_3_TO_5, false, "one-line-on-crash", 'k', required_argument, 0,
-     eArgTypeNone, "When in batch mode, tells the debugger to execute this "
-                   "one-line lldb command if the target crashes."},
+     eArgTypeNone,
+     "When in batch mode, tells the debugger to execute this "
+     "one-line lldb command if the target crashes."},
     {LLDB_3_TO_5, false, "source-on-crash", 'K', required_argument, 0,
-     eArgTypeFilename, "When in batch mode, tells the debugger to source this "
-                       "file of lldb commands if the target crashes."},
+     eArgTypeFilename,
+     "When in batch mode, tells the debugger to source this "
+     "file of lldb commands if the target crashes."},
     {LLDB_3_TO_5, false, "source-quietly", 'Q', no_argument, 0, eArgTypeNone,
      "Tells the debugger to execute this one-line lldb command before any file "
      "provided on the command line has been loaded."},
@@ -159,6 +163,9 @@ static constexpr OptionDefinition g_opti
      "extensions have been implemented."},
     {LLDB_3_TO_5, false, "debug", 'd', no_argument, 0, eArgTypeNone,
      "Tells the debugger to print out extra information for debugging itself."},
+    {LLDB_3_TO_5, false, "reproducer", 'z', required_argument, 0,
+     eArgTypeFilename,
+     "Tells the debugger to use the fullpath to <path> as a reproducer."},
     {LLDB_OPT_SET_7, true, "repl", 'r', optional_argument, 0, eArgTypeNone,
      "Runs lldb in REPL mode with a stub process."},
     {LLDB_OPT_SET_7, true, "repl-language", 'R', required_argument, 0,
@@ -724,6 +731,16 @@ SBError Driver::ParseArgs(int argc, cons
           m_option_data.m_debug_mode = true;
           break;
 
+        case 'z': {
+          SBFileSpec file(optarg);
+          if (file.Exists()) {
+            m_debugger.SetReproducerPath(optarg);
+          } else
+            error.SetErrorStringWithFormat("file specified in --reproducer "
+                                           "(-z) option doesn't exist: '%s'",
+                                           optarg);
+        } break;
+
         case 'Q':
           m_option_data.m_source_quietly = true;
           break;

Modified: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp (original)
+++ lldb/trunk/unittests/Process/gdb-remote/GDBRemoteClientBaseTest.cpp Tue Nov 13 11:18:16 2018
@@ -48,7 +48,8 @@ struct TestClient : public GDBRemoteClie
 class GDBRemoteClientBaseTest : public GDBRemoteTest {
 public:
   void SetUp() override {
-    ASSERT_THAT_ERROR(Connect(client, server), llvm::Succeeded());
+    ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
+                      llvm::Succeeded());
     ASSERT_EQ(TestClient::eBroadcastBitRunPacketSent,
               listener_sp->StartListeningForEvents(
                   &client, TestClient::eBroadcastBitRunPacketSent));

Modified: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (original)
+++ lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp Tue Nov 13 11:18:16 2018
@@ -63,7 +63,8 @@ std::string one_register_hex = "41424344
 class GDBRemoteCommunicationClientTest : public GDBRemoteTest {
 public:
   void SetUp() override {
-    ASSERT_THAT_ERROR(Connect(client, server), llvm::Succeeded());
+    ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
+                      llvm::Succeeded());
   }
 
 protected:

Modified: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp (original)
+++ lldb/trunk/unittests/Process/gdb-remote/GDBRemoteCommunicationTest.cpp Tue Nov 13 11:18:16 2018
@@ -30,7 +30,8 @@ public:
 class GDBRemoteCommunicationTest : public GDBRemoteTest {
 public:
   void SetUp() override {
-    ASSERT_THAT_ERROR(Connect(client, server), llvm::Succeeded());
+    ASSERT_THAT_ERROR(GDBRemoteCommunication::ConnectLocally(client, server),
+                      llvm::Succeeded());
   }
 
 protected:

Modified: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp (original)
+++ lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.cpp Tue Nov 13 11:18:16 2018
@@ -9,11 +9,6 @@
 
 #include "GDBRemoteTestUtils.h"
 
-#include "lldb/Host/common/TCPSocket.h"
-#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
-
-#include <future>
-
 namespace lldb_private {
 namespace process_gdb_remote {
 
@@ -30,34 +25,5 @@ void GDBRemoteTest::TearDownTestCase() {
 #endif
 }
 
-llvm::Error GDBRemoteTest::Connect(GDBRemoteCommunication &client,
-                                   GDBRemoteCommunication &server) {
-  bool child_processes_inherit = false;
-  TCPSocket listen_socket(true, child_processes_inherit);
-  if (llvm::Error error = listen_socket.Listen("127.0.0.1:0", 5).ToError())
-    return error;
-
-  Socket *accept_socket;
-  std::future<Status> accept_status = std::async(
-      std::launch::async, [&] { return listen_socket.Accept(accept_socket); });
-
-  llvm::SmallString<32> remote_addr;
-  llvm::raw_svector_ostream(remote_addr)
-      << "connect://localhost:" << listen_socket.GetLocalPortNumber();
-
-  std::unique_ptr<ConnectionFileDescriptor> conn_up(
-      new ConnectionFileDescriptor());
-  if (conn_up->Connect(remote_addr, nullptr) != lldb::eConnectionStatusSuccess)
-    return llvm::make_error<llvm::StringError>("Unable to connect",
-                                               llvm::inconvertibleErrorCode());
-
-  client.SetConnection(conn_up.release());
-  if (llvm::Error error = accept_status.get().ToError())
-    return error;
-
-  server.SetConnection(new ConnectionFileDescriptor(accept_socket));
-  return llvm::Error::success();
-}
-
 } // namespace process_gdb_remote
 } // namespace lldb_private

Modified: lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.h?rev=346780&r1=346779&r2=346780&view=diff
==============================================================================
--- lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.h (original)
+++ lldb/trunk/unittests/Process/gdb-remote/GDBRemoteTestUtils.h Tue Nov 13 11:18:16 2018
@@ -20,10 +20,6 @@ class GDBRemoteTest : public testing::Te
 public:
   static void SetUpTestCase();
   static void TearDownTestCase();
-
-protected:
-  llvm::Error Connect(GDBRemoteCommunication &client,
-                      GDBRemoteCommunication &server);
 };
 
 struct MockServer : public GDBRemoteCommunicationServer {




More information about the lldb-commits mailing list