<div dir="ltr">For now I've gone ahead and reverted thusly:<div><br></div><div>echristo@athyra ~/r/llvm-project> git push<br>To github.com:llvm/llvm-project.git<br>   a2602bdd731..c9e0b354e27  master -> master<br></div><div><br></div><div>Sorry for the very quick turnaround.</div><div><br></div><div>-eric</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 10, 2019 at 12:21 PM Eric Christopher <<a href="mailto:echristo@gmail.com">echristo@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">Hi Jonas,<div><br></div><div>This appears to be causing crashes in a release asserts build:</div><div><br></div><div> #3 0x00007efe57b613a0 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x123a0)<br> #4 0x00007efe54178d00 llvm::raw_ostream::operator<<(llvm::StringRef) sources/llvm-project/llvm/include/llvm/Support/raw_ostream.h:190:7<br> #5 0x00007efe54178d00 llvm::yaml::Output::output(llvm::StringRef) sources/llvm-project/llvm/lib/Support/YAMLTraits.cpp:751:7<br> #6 0x00007efe54178d00 llvm::yaml::Output::outputUpToEndOfLine(llvm::StringRef) sources/llvm-project/llvm/lib/Support/YAMLTraits.cpp:755:3<br> #7 0x00007efe54178d00 llvm::yaml::Output::beginDocuments() sources/llvm-project/llvm/lib/Support/YAMLTraits.cpp:537:3<br> #8 0x00007efe53fc5d7e std::enable_if<has_MappingTraits<lldb_private::GDBRemotePacket, llvm::yaml::EmptyContext>::value, llvm::yaml::Output&>::type llvm::yaml::operator<<<lldb_private::GDBRemotePacket>(llvm::yaml::Output&, lldb_private::GDBRemotePacket&) sources/llvm-project/llvm/include/llvm/Support/YAMLTraits.h:1796:13<br> #9 0x00007efe53fc5d7e lldb_private::GDBRemotePacket::Serialize(llvm::raw_ostream&) const sources/llvm-project/lldb/source/Utility/GDBRemote.cpp:50:8<br></div><div>...</div><div><br></div><div>want to revert and see what's going on? Happy to help if you can't duplicate?</div><div><br></div><div>Thanks!</div><div><br></div><div>-eric </div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Dec 10, 2019 at 11:17 AM Jonas Devlieghere via lldb-commits <<a href="mailto:lldb-commits@lists.llvm.org" target="_blank">lldb-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Jonas Devlieghere<br>
Date: 2019-12-10T11:16:52-08:00<br>
New Revision: e81268d03e73aef4f9c7bd8ece8ad02f5b017dcf<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/e81268d03e73aef4f9c7bd8ece8ad02f5b017dcf" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/e81268d03e73aef4f9c7bd8ece8ad02f5b017dcf</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/e81268d03e73aef4f9c7bd8ece8ad02f5b017dcf.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/e81268d03e73aef4f9c7bd8ece8ad02f5b017dcf.diff</a><br>
<br>
LOG: [lldb/Reproducers] Support multiple GDB remotes<br>
<br>
When running the test suite with always capture on, a handful of tests<br>
are failing because they have multiple targets and therefore multiple<br>
GDB remote connections. The current reproducer infrastructure is capable<br>
of dealing with that.<br>
<br>
This patch reworks the GDB remote provider to support multiple GDB<br>
remote connections, similar to how the reproducers support shadowing<br>
multiple command interpreter inputs. The provider now keeps a list of<br>
packet recorders which deal with a single GDB remote connection. During<br>
replay we rely on the order of creation to match the number of packets<br>
to the GDB remote connection.<br>
<br>
Differential revision: <a href="https://reviews.llvm.org/D71105" rel="noreferrer" target="_blank">https://reviews.llvm.org/D71105</a><br>
<br>
Added: <br>
    lldb/test/Shell/Reproducer/Inputs/MultipleTargetsCapture.in<br>
    lldb/test/Shell/Reproducer/TestMultipleTargets.test<br>
<br>
Modified: <br>
    lldb/include/lldb/Utility/GDBRemote.h<br>
    lldb/include/lldb/Utility/Reproducer.h<br>
    lldb/source/API/SBDebugger.cpp<br>
    lldb/source/Commands/CommandObjectReproducer.cpp<br>
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp<br>
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h<br>
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp<br>
    lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h<br>
    lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp<br>
    lldb/source/Utility/GDBRemote.cpp<br>
    lldb/source/Utility/Reproducer.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/lldb/include/lldb/Utility/GDBRemote.h b/lldb/include/lldb/Utility/GDBRemote.h<br>
index b4adeb368524..21b2c8cd73cd 100644<br>
--- a/lldb/include/lldb/Utility/GDBRemote.h<br>
+++ b/lldb/include/lldb/Utility/GDBRemote.h<br>
@@ -9,6 +9,8 @@<br>
 #ifndef liblldb_GDBRemote_h_<br>
 #define liblldb_GDBRemote_h_<br>
<br>
+#include "lldb/Utility/FileSpec.h"<br>
+#include "lldb/Utility/Reproducer.h"<br>
 #include "lldb/Utility/StreamString.h"<br>
 #include "lldb/lldb-enumerations.h"<br>
 #include "lldb/lldb-public.h"<br>
@@ -69,7 +71,6 @@ struct GDBRemotePacket {<br>
     std::string data;<br>
   };<br>
<br>
-  void Serialize(llvm::raw_ostream &strm) const;<br>
   void Dump(Stream &strm) const;<br>
<br>
   BinaryData packet;<br>
@@ -82,6 +83,46 @@ struct GDBRemotePacket {<br>
   llvm::StringRef GetTypeStr() const;<br>
 };<br>
<br>
+namespace repro {<br>
+class PacketRecorder : public AbstractRecorder {<br>
+public:<br>
+  PacketRecorder(const FileSpec &filename, std::error_code &ec)<br>
+      : AbstractRecorder(filename, ec) {}<br>
+<br>
+  static llvm::Expected<std::unique_ptr<PacketRecorder>><br>
+  Create(const FileSpec &filename);<br>
+<br>
+  void Record(const GDBRemotePacket &packet);<br>
+};<br>
+<br>
+class GDBRemoteProvider : public repro::Provider<GDBRemoteProvider> {<br>
+public:<br>
+  struct Info {<br>
+    static const char *name;<br>
+    static const char *file;<br>
+  };<br>
+<br>
+  GDBRemoteProvider(const FileSpec &directory) : Provider(directory) {}<br>
+<br>
+  llvm::raw_ostream *GetHistoryStream();<br>
+  PacketRecorder *GetNewPacketRecorder();<br>
+<br>
+  void SetCallback(std::function<void()> callback) {<br>
+    m_callback = std::move(callback);<br>
+  }<br>
+<br>
+  void Keep() override;<br>
+  void Discard() override;<br>
+<br>
+  static char ID;<br>
+<br>
+private:<br>
+  std::function<void()> m_callback;<br>
+  std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;<br>
+  std::vector<std::unique_ptr<PacketRecorder>> m_packet_recorders;<br>
+};<br>
+<br>
+} // namespace repro<br>
 } // namespace lldb_private<br>
<br>
 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::GDBRemotePacket)<br>
<br>
diff  --git a/lldb/include/lldb/Utility/Reproducer.h b/lldb/include/lldb/Utility/Reproducer.h<br>
index ddb1f45a7219..0d23fe8571ff 100644<br>
--- a/lldb/include/lldb/Utility/Reproducer.h<br>
+++ b/lldb/include/lldb/Utility/Reproducer.h<br>
@@ -153,24 +153,13 @@ class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {<br>
   static char ID;<br>
 };<br>
<br>
-class DataRecorder {<br>
-public:<br>
-  DataRecorder(const FileSpec &filename, std::error_code &ec)<br>
+class AbstractRecorder {<br>
+protected:<br>
+  AbstractRecorder(const FileSpec &filename, std::error_code &ec)<br>
       : m_filename(filename.GetFilename().GetStringRef()),<br>
         m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}<br>
<br>
-  static llvm::Expected<std::unique_ptr<DataRecorder>><br>
-  Create(const FileSpec &filename);<br>
-<br>
-  template <typename T> void Record(const T &t, bool newline = false) {<br>
-    if (!m_record)<br>
-      return;<br>
-    m_os << t;<br>
-    if (newline)<br>
-      m_os << '\n';<br>
-    m_os.flush();<br>
-  }<br>
-<br>
+public:<br>
   const FileSpec &GetFilename() { return m_filename; }<br>
<br>
   void Stop() {<br>
@@ -180,10 +169,30 @@ class DataRecorder {<br>
<br>
 private:<br>
   FileSpec m_filename;<br>
+<br>
+protected:<br>
   llvm::raw_fd_ostream m_os;<br>
   bool m_record;<br>
 };<br>
<br>
+class DataRecorder : public AbstractRecorder {<br>
+public:<br>
+  DataRecorder(const FileSpec &filename, std::error_code &ec)<br>
+      : AbstractRecorder(filename, ec) {}<br>
+<br>
+  static llvm::Expected<std::unique_ptr<DataRecorder>><br>
+  Create(const FileSpec &filename);<br>
+<br>
+  template <typename T> void Record(const T &t, bool newline = false) {<br>
+    if (!m_record)<br>
+      return;<br>
+    m_os << t;<br>
+    if (newline)<br>
+      m_os << '\n';<br>
+    m_os.flush();<br>
+  }<br>
+};<br>
+<br>
 class CommandProvider : public Provider<CommandProvider> {<br>
 public:<br>
   struct Info {<br>
@@ -204,32 +213,6 @@ class CommandProvider : public Provider<CommandProvider> {<br>
   std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;<br>
 };<br>
<br>
-class ProcessGDBRemoteProvider<br>
-    : public repro::Provider<ProcessGDBRemoteProvider> {<br>
-public:<br>
-  struct Info {<br>
-    static const char *name;<br>
-    static const char *file;<br>
-  };<br>
-<br>
-  ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {}<br>
-<br>
-  llvm::raw_ostream *GetHistoryStream();<br>
-<br>
-  void SetCallback(std::function<void()> callback) {<br>
-    m_callback = std::move(callback);<br>
-  }<br>
-<br>
-  void Keep() override { m_callback(); }<br>
-  void Discard() override { m_callback(); }<br>
-<br>
-  static char ID;<br>
-<br>
-private:<br>
-  std::function<void()> m_callback;<br>
-  std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;<br>
-};<br>
-<br>
 /// The generator is responsible for the logic needed to generate a<br>
 /// reproducer. For doing so it relies on providers, who serialize data that<br>
 /// is necessary for reproducing  a failure.<br>
@@ -359,13 +342,43 @@ class Reproducer {<br>
   mutable std::mutex m_mutex;<br>
 };<br>
<br>
-/// Helper class for replaying commands through the reproducer.<br>
-class CommandLoader {<br>
+template <typename T> class MultiLoader {<br>
 public:<br>
-  CommandLoader(std::vector<std::string> files) : m_files(files) {}<br>
+  MultiLoader(std::vector<std::string> files) : m_files(files) {}<br>
+<br>
+  static std::unique_ptr<MultiLoader> Create(Loader *loader) {<br>
+    if (!loader)<br>
+      return {};<br>
+<br>
+    FileSpec file = loader->GetFile<typename T::Info>();<br>
+    if (!file)<br>
+      return {};<br>
+<br>
+    auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());<br>
+    if (auto err = error_or_file.getError())<br>
+      return {};<br>
<br>
-  static std::unique_ptr<CommandLoader> Create(Loader *loader);<br>
-  llvm::Optional<std::string> GetNextFile();<br>
+    std::vector<std::string> files;<br>
+    llvm::yaml::Input yin((*error_or_file)->getBuffer());<br>
+    yin >> files;<br>
+<br>
+    if (auto err = yin.error())<br>
+      return {};<br>
+<br>
+    for (auto &file : files) {<br>
+      FileSpec absolute_path =<br>
+          loader->GetRoot().CopyByAppendingPathComponent(file);<br>
+      file = absolute_path.GetPath();<br>
+    }<br>
+<br>
+    return std::make_unique<MultiLoader<T>>(std::move(files));<br>
+  }<br>
+<br>
+  llvm::Optional<std::string> GetNextFile() {<br>
+    if (m_index >= m_files.size())<br>
+      return {};<br>
+    return m_files[m_index++];<br>
+  }<br>
<br>
 private:<br>
   std::vector<std::string> m_files;<br>
<br>
diff  --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp<br>
index 090a3a57a2f4..e938727391e0 100644<br>
--- a/lldb/source/API/SBDebugger.cpp<br>
+++ b/lldb/source/API/SBDebugger.cpp<br>
@@ -315,8 +315,9 @@ SBError SBDebugger::SetInputFile(SBFile file) {<br>
<br>
   FileSP file_sp = file.m_opaque_sp;<br>
<br>
-  static std::unique_ptr<repro::CommandLoader> loader =<br>
-      repro::CommandLoader::Create(repro::Reproducer::Instance().GetLoader());<br>
+  static std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> loader =<br>
+      repro::MultiLoader<repro::CommandProvider>::Create(<br>
+          repro::Reproducer::Instance().GetLoader());<br>
   if (loader) {<br>
     llvm::Optional<std::string> nextfile = loader->GetNextFile();<br>
     FILE *fh = nextfile ? FileSystem::Instance().Fopen(nextfile->c_str(), "r")<br>
<br>
diff  --git a/lldb/source/Commands/CommandObjectReproducer.cpp b/lldb/source/Commands/CommandObjectReproducer.cpp<br>
index a4c69db492da..0f05c564a0d1 100644<br>
--- a/lldb/source/Commands/CommandObjectReproducer.cpp<br>
+++ b/lldb/source/Commands/CommandObjectReproducer.cpp<br>
@@ -407,10 +407,9 @@ class CommandObjectReproducerDump : public CommandObjectParsed {<br>
       return true;<br>
     }<br>
     case eReproducerProviderCommands: {<br>
-      // Create a new command loader.<br>
-      std::unique_ptr<repro::CommandLoader> command_loader =<br>
-          repro::CommandLoader::Create(loader);<br>
-      if (!command_loader) {<br>
+      std::unique_ptr<repro::MultiLoader<repro::CommandProvider>> multi_loader =<br>
+          repro::MultiLoader<repro::CommandProvider>::Create(loader);<br>
+      if (!multi_loader) {<br>
         SetError(result,<br>
                  make_error<StringError>(llvm::inconvertibleErrorCode(),<br>
                                          "Unable to create command loader."));<br>
@@ -418,9 +417,8 @@ class CommandObjectReproducerDump : public CommandObjectParsed {<br>
       }<br>
<br>
       // Iterate over the command files and dump them.<br>
-      while (true) {<br>
-        llvm::Optional<std::string> command_file =<br>
-            command_loader->GetNextFile();<br>
+      llvm::Optional<std::string> command_file;<br>
+      while ((command_file = multi_loader->GetNextFile())) {<br>
         if (!command_file)<br>
           break;<br>
<br>
@@ -436,24 +434,29 @@ class CommandObjectReproducerDump : public CommandObjectParsed {<br>
       return true;<br>
     }<br>
     case eReproducerProviderGDB: {<br>
-      FileSpec gdb_file = loader->GetFile<ProcessGDBRemoteProvider::Info>();<br>
-      auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath());<br>
-      if (auto err = error_or_file.getError()) {<br>
-        SetError(result, errorCodeToError(err));<br>
-        return false;<br>
-      }<br>
+      std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>><br>
+          multi_loader =<br>
+              repro::MultiLoader<repro::GDBRemoteProvider>::Create(loader);<br>
+      llvm::Optional<std::string> gdb_file;<br>
+      while ((gdb_file = multi_loader->GetNextFile())) {<br>
+        auto error_or_file = MemoryBuffer::getFile(*gdb_file);<br>
+        if (auto err = error_or_file.getError()) {<br>
+          SetError(result, errorCodeToError(err));<br>
+          return false;<br>
+        }<br>
<br>
-      std::vector<GDBRemotePacket> packets;<br>
-      yaml::Input yin((*error_or_file)->getBuffer());<br>
-      yin >> packets;<br>
+        std::vector<GDBRemotePacket> packets;<br>
+        yaml::Input yin((*error_or_file)->getBuffer());<br>
+        yin >> packets;<br>
<br>
-      if (auto err = yin.error()) {<br>
-        SetError(result, errorCodeToError(err));<br>
-        return false;<br>
-      }<br>
+        if (auto err = yin.error()) {<br>
+          SetError(result, errorCodeToError(err));<br>
+          return false;<br>
+        }<br>
<br>
-      for (GDBRemotePacket &packet : packets) {<br>
-        packet.Dump(result.GetOutputStream());<br>
+        for (GDBRemotePacket &packet : packets) {<br>
+          packet.Dump(result.GetOutputStream());<br>
+        }<br>
       }<br>
<br>
       result.SetStatus(eReturnStatusSuccessFinishResult);<br>
<br>
diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp<br>
index 144ae103faa4..0a98f6a15d75 100644<br>
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp<br>
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp<br>
@@ -31,6 +31,7 @@<br>
 #include "lldb/Utility/FileSpec.h"<br>
 #include "lldb/Utility/Log.h"<br>
 #include "lldb/Utility/RegularExpression.h"<br>
+#include "lldb/Utility/Reproducer.h"<br>
 #include "lldb/Utility/StreamString.h"<br>
 #include "llvm/ADT/SmallString.h"<br>
 #include "llvm/Support/ScopedPrinter.h"<br>
@@ -1243,8 +1244,9 @@ Status GDBRemoteCommunication::StartDebugserverProcess(<br>
<br>
 void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); }<br>
<br>
-void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) {<br>
-  m_history.SetStream(strm);<br>
+void GDBRemoteCommunication::SetPacketRecorder(<br>
+    repro::PacketRecorder *recorder) {<br>
+  m_history.SetRecorder(recorder);<br>
 }<br>
<br>
 llvm::Error<br>
<br>
diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h<br>
index bb777a5c26a7..0b670018bd69 100644<br>
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h<br>
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h<br>
@@ -27,6 +27,9 @@<br>
 #include "lldb/lldb-public.h"<br>
<br>
 namespace lldb_private {<br>
+namespace repro {<br>
+class PacketRecorder;<br>
+}<br>
 namespace process_gdb_remote {<br>
<br>
 enum GDBStoppointType {<br>
@@ -133,7 +136,8 @@ class GDBRemoteCommunication : public Communication {<br>
                          // fork/exec to avoid having to connect/accept<br>
<br>
   void DumpHistory(Stream &strm);<br>
-  void SetHistoryStream(llvm::raw_ostream *strm);<br>
+<br>
+  void SetPacketRecorder(repro::PacketRecorder *recorder);<br>
<br>
   static llvm::Error ConnectLocally(GDBRemoteCommunication &client,<br>
                                     GDBRemoteCommunication &server);<br>
<br>
diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp<br>
index d2cc32f63f20..9e5646985f87 100644<br>
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp<br>
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp<br>
@@ -40,8 +40,8 @@ void GDBRemoteCommunicationHistory::AddPacket(char packet_char,<br>
   m_packets[idx].bytes_transmitted = bytes_transmitted;<br>
   m_packets[idx].packet_idx = m_total_packet_count;<br>
   m_packets[idx].tid = llvm::get_threadid();<br>
-  if (m_stream)<br>
-    m_packets[idx].Serialize(*m_stream);<br>
+  if (m_recorder)<br>
+    m_recorder->Record(m_packets[idx]);<br>
 }<br>
<br>
 void GDBRemoteCommunicationHistory::AddPacket(const std::string &src,<br>
@@ -58,8 +58,8 @@ void GDBRemoteCommunicationHistory::AddPacket(const std::string &src,<br>
   m_packets[idx].bytes_transmitted = bytes_transmitted;<br>
   m_packets[idx].packet_idx = m_total_packet_count;<br>
   m_packets[idx].tid = llvm::get_threadid();<br>
-  if (m_stream)<br>
-    m_packets[idx].Serialize(*m_stream);<br>
+  if (m_recorder)<br>
+    m_recorder->Record(m_packets[idx]);<br>
 }<br>
<br>
 void GDBRemoteCommunicationHistory::Dump(Stream &strm) const {<br>
<br>
diff  --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h<br>
index c006fbd34a4b..ee265ef86dff 100644<br>
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h<br>
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h<br>
@@ -13,11 +13,15 @@<br>
 #include <vector><br>
<br>
 #include "lldb/Utility/GDBRemote.h"<br>
+#include "lldb/Utility/Reproducer.h"<br>
 #include "lldb/lldb-public.h"<br>
 #include "llvm/Support/YAMLTraits.h"<br>
 #include "llvm/Support/raw_ostream.h"<br>
<br>
 namespace lldb_private {<br>
+namespace repro {<br>
+class PacketRecorder;<br>
+}<br>
 namespace process_gdb_remote {<br>
<br>
 /// The history keeps a circular buffer of GDB remote packets. The history is<br>
@@ -41,7 +45,7 @@ class GDBRemoteCommunicationHistory {<br>
   void Dump(Log *log) const;<br>
   bool DidDumpToLog() const { return m_dumped_to_log; }<br>
<br>
-  void SetStream(llvm::raw_ostream *strm) { m_stream = strm; }<br>
+  void SetRecorder(repro::PacketRecorder *recorder) { m_recorder = recorder; }<br>
<br>
 private:<br>
   uint32_t GetFirstSavedPacketIndex() const {<br>
@@ -73,7 +77,7 @@ class GDBRemoteCommunicationHistory {<br>
   uint32_t m_curr_idx;<br>
   uint32_t m_total_packet_count;<br>
   mutable bool m_dumped_to_log;<br>
-  llvm::raw_ostream *m_stream = nullptr;<br>
+  repro::PacketRecorder *m_recorder = nullptr;<br>
 };<br>
<br>
 } // namespace process_gdb_remote<br>
<br>
diff  --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp<br>
index dfef06aa6eaf..95f3d1fcc53a 100644<br>
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp<br>
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp<br>
@@ -279,12 +279,9 @@ ProcessGDBRemote::ProcessGDBRemote(lldb::TargetSP target_sp,<br>
                                    "async thread did exit");<br>
<br>
   if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) {<br>
-    repro::ProcessGDBRemoteProvider &provider =<br>
-        g->GetOrCreate<repro::ProcessGDBRemoteProvider>();<br>
-    // Set the history stream to the stream owned by the provider.<br>
-    m_gdb_comm.SetHistoryStream(provider.GetHistoryStream());<br>
-    // Make sure to clear the stream again when we're finished.<br>
-    provider.SetCallback([&]() { m_gdb_comm.SetHistoryStream(nullptr); });<br>
+    repro::GDBRemoteProvider &provider =<br>
+        g->GetOrCreate<repro::GDBRemoteProvider>();<br>
+    m_gdb_comm.SetPacketRecorder(provider.GetNewPacketRecorder());<br>
   }<br>
<br>
   Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC));<br>
@@ -3362,17 +3359,20 @@ Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) {<br>
   if (!loader)<br>
     return Status("No loader provided.");<br>
<br>
-  // Construct replay history path.<br>
-  FileSpec history_file =<br>
-      loader->GetFile<repro::ProcessGDBRemoteProvider::Info>();<br>
-  if (!history_file)<br>
-    return Status("No provider for gdb-remote.");<br>
+  static std::unique_ptr<repro::MultiLoader<repro::GDBRemoteProvider>><br>
+      multi_loader = repro::MultiLoader<repro::GDBRemoteProvider>::Create(<br>
+          repro::Reproducer::Instance().GetLoader());<br>
<br>
-  // Enable replay mode.<br>
-  m_replay_mode = true;<br>
+  if (!multi_loader)<br>
+    return Status("No gdb remote provider found.");<br>
+<br>
+  llvm::Optional<std::string> history_file = multi_loader->GetNextFile();<br>
+  if (!history_file)<br>
+    return Status("No gdb remote packet log found.");<br>
<br>
   // Load replay history.<br>
-  if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file))<br>
+  if (auto error =<br>
+          m_gdb_replay_server.LoadReplayHistory(FileSpec(*history_file)))<br>
     return Status("Unable to load replay history");<br>
<br>
   // Make a local connection.<br>
@@ -3380,6 +3380,9 @@ Status ProcessGDBRemote::ConnectToReplayServer(repro::Loader *loader) {<br>
                                                           m_gdb_replay_server))<br>
     return Status("Unable to connect to replay server");<br>
<br>
+  // Enable replay mode.<br>
+  m_replay_mode = true;<br>
+<br>
   // Start server thread.<br>
   m_gdb_replay_server.StartAsyncThread();<br>
<br>
<br>
diff  --git a/lldb/source/Utility/GDBRemote.cpp b/lldb/source/Utility/GDBRemote.cpp<br>
index 85c4bc69a8d1..54f3a3cd8a86 100644<br>
--- a/lldb/source/Utility/GDBRemote.cpp<br>
+++ b/lldb/source/Utility/GDBRemote.cpp<br>
@@ -14,6 +14,7 @@<br>
 #include <stdio.h><br>
<br>
 using namespace lldb;<br>
+using namespace lldb_private::repro;<br>
 using namespace lldb_private;<br>
 using namespace llvm;<br>
<br>
@@ -45,12 +46,6 @@ int StreamGDBRemote::PutEscapedBytes(const void *s, size_t src_len) {<br>
   return bytes_written;<br>
 }<br>
<br>
-void GDBRemotePacket::Serialize(raw_ostream &strm) const {<br>
-  yaml::Output yout(strm);<br>
-  yout << const_cast<GDBRemotePacket &>(*this);<br>
-  strm.flush();<br>
-}<br>
-<br>
 llvm::StringRef GDBRemotePacket::GetTypeStr() const {<br>
   switch (type) {<br>
   case GDBRemotePacket::ePacketTypeSend:<br>
@@ -103,3 +98,66 @@ yaml::MappingTraits<GDBRemotePacket>::validate(IO &io,<br>
<br>
   return {};<br>
 }<br>
+<br>
+void GDBRemoteProvider::Keep() {<br>
+  std::vector<std::string> files;<br>
+  for (auto &recorder : m_packet_recorders) {<br>
+    files.push_back(recorder->GetFilename().GetPath());<br>
+  }<br>
+<br>
+  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);<br>
+  std::error_code ec;<br>
+  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);<br>
+  if (ec)<br>
+    return;<br>
+  yaml::Output yout(os);<br>
+  yout << files;<br>
+}<br>
+<br>
+void GDBRemoteProvider::Discard() { m_packet_recorders.clear(); }<br>
+<br>
+llvm::Expected<std::unique_ptr<PacketRecorder>><br>
+PacketRecorder::Create(const FileSpec &filename) {<br>
+  std::error_code ec;<br>
+  auto recorder = std::make_unique<PacketRecorder>(std::move(filename), ec);<br>
+  if (ec)<br>
+    return llvm::errorCodeToError(ec);<br>
+  return std::move(recorder);<br>
+}<br>
+<br>
+PacketRecorder *GDBRemoteProvider::GetNewPacketRecorder() {<br>
+  std::size_t i = m_packet_recorders.size() + 1;<br>
+  std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +<br>
+                          llvm::Twine(i) + llvm::Twine(".yaml"))<br>
+                             .str();<br>
+  auto recorder_or_error =<br>
+      PacketRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));<br>
+  if (!recorder_or_error) {<br>
+    llvm::consumeError(recorder_or_error.takeError());<br>
+    return nullptr;<br>
+  }<br>
+<br>
+  m_packet_recorders.push_back(std::move(*recorder_or_error));<br>
+  return m_packet_recorders.back().get();<br>
+}<br>
+<br>
+void PacketRecorder::Record(const GDBRemotePacket &packet) {<br>
+  if (!m_record)<br>
+    return;<br>
+  yaml::Output yout(m_os);<br>
+  yout << const_cast<GDBRemotePacket &>(packet);<br>
+  m_os.flush();<br>
+}<br>
+<br>
+llvm::raw_ostream *GDBRemoteProvider::GetHistoryStream() {<br>
+  FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file);<br>
+<br>
+  std::error_code EC;<br>
+  m_stream_up = std::make_unique<raw_fd_ostream>(history_file.GetPath(), EC,<br>
+                                                 sys::fs::OpenFlags::OF_Text);<br>
+  return m_stream_up.get();<br>
+}<br>
+<br>
+char GDBRemoteProvider::ID = 0;<br>
+const char *GDBRemoteProvider::Info::file = "gdb-remote.yaml";<br>
+const char *GDBRemoteProvider::Info::name = "gdb-remote";<br>
<br>
diff  --git a/lldb/source/Utility/Reproducer.cpp b/lldb/source/Utility/Reproducer.cpp<br>
index 8a28e9b13675..b11e1a577ed2 100644<br>
--- a/lldb/source/Utility/Reproducer.cpp<br>
+++ b/lldb/source/Utility/Reproducer.cpp<br>
@@ -255,7 +255,7 @@ DataRecorder::Create(const FileSpec &filename) {<br>
 DataRecorder *CommandProvider::GetNewDataRecorder() {<br>
   std::size_t i = m_data_recorders.size() + 1;<br>
   std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +<br>
-                          llvm::Twine(i) + llvm::Twine(".txt"))<br>
+                          llvm::Twine(i) + llvm::Twine(".yaml"))<br>
                              .str();<br>
   auto recorder_or_error =<br>
       DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));<br>
@@ -304,53 +304,9 @@ void WorkingDirectoryProvider::Keep() {<br>
   os << m_cwd << "\n";<br>
 }<br>
<br>
-llvm::raw_ostream *ProcessGDBRemoteProvider::GetHistoryStream() {<br>
-  FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file);<br>
-<br>
-  std::error_code EC;<br>
-  m_stream_up = std::make_unique<raw_fd_ostream>(history_file.GetPath(), EC,<br>
-                                                 sys::fs::OpenFlags::OF_Text);<br>
-  return m_stream_up.get();<br>
-}<br>
-<br>
-std::unique_ptr<CommandLoader> CommandLoader::Create(Loader *loader) {<br>
-  if (!loader)<br>
-    return {};<br>
-<br>
-  FileSpec file = loader->GetFile<repro::CommandProvider::Info>();<br>
-  if (!file)<br>
-    return {};<br>
-<br>
-  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());<br>
-  if (auto err = error_or_file.getError())<br>
-    return {};<br>
-<br>
-  std::vector<std::string> files;<br>
-  llvm::yaml::Input yin((*error_or_file)->getBuffer());<br>
-  yin >> files;<br>
-<br>
-  if (auto err = yin.error())<br>
-    return {};<br>
-<br>
-  for (auto &file : files) {<br>
-    FileSpec absolute_path =<br>
-        loader->GetRoot().CopyByAppendingPathComponent(file);<br>
-    file = absolute_path.GetPath();<br>
-  }<br>
-<br>
-  return std::make_unique<CommandLoader>(std::move(files));<br>
-}<br>
-<br>
-llvm::Optional<std::string> CommandLoader::GetNextFile() {<br>
-  if (m_index >= m_files.size())<br>
-    return {};<br>
-  return m_files[m_index++];<br>
-}<br>
-<br>
 void ProviderBase::anchor() {}<br>
 char CommandProvider::ID = 0;<br>
 char FileProvider::ID = 0;<br>
-char ProcessGDBRemoteProvider::ID = 0;<br>
 char ProviderBase::ID = 0;<br>
 char VersionProvider::ID = 0;<br>
 char WorkingDirectoryProvider::ID = 0;<br>
@@ -358,8 +314,6 @@ const char *CommandProvider::Info::file = "command-interpreter.yaml";<br>
 const char *CommandProvider::Info::name = "command-interpreter";<br>
 const char *FileProvider::Info::file = "files.yaml";<br>
 const char *FileProvider::Info::name = "files";<br>
-const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml";<br>
-const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote";<br>
 const char *VersionProvider::Info::file = "version.txt";<br>
 const char *VersionProvider::Info::name = "version";<br>
 const char *WorkingDirectoryProvider::Info::file = "cwd.txt";<br>
<br>
diff  --git a/lldb/test/Shell/Reproducer/Inputs/MultipleTargetsCapture.in b/lldb/test/Shell/Reproducer/Inputs/MultipleTargetsCapture.in<br>
new file mode 100644<br>
index 000000000000..c78d6276c89f<br>
--- /dev/null<br>
+++ b/lldb/test/Shell/Reproducer/Inputs/MultipleTargetsCapture.in<br>
@@ -0,0 +1,12 @@<br>
+target select 0<br>
+breakpoint set -f simple.c -l 12<br>
+run<br>
+target select 1<br>
+breakpoint set -f simple.c -l 16<br>
+run<br>
+target select 0<br>
+cont<br>
+target select 1<br>
+cont<br>
+reproducer status<br>
+reproducer generate<br>
<br>
diff  --git a/lldb/test/Shell/Reproducer/TestMultipleTargets.test b/lldb/test/Shell/Reproducer/TestMultipleTargets.test<br>
new file mode 100644<br>
index 000000000000..f36dbf6b5c44<br>
--- /dev/null<br>
+++ b/lldb/test/Shell/Reproducer/TestMultipleTargets.test<br>
@@ -0,0 +1,23 @@<br>
+# UNSUPPORTED: system-windows, system-freebsd<br>
+<br>
+# This tests the replaying with multiple targets.<br>
+<br>
+# RUN: %clang_host %S/Inputs/simple.c -g -o %t.out<br>
+<br>
+# RUN: rm -rf %t.repro<br>
+# RUN: %lldb -x -b --capture --capture-path %t.repro -o 'target create %t.out' -o 'target create %t.out' -s %S/Inputs/MultipleTargetsCapture.in  | FileCheck %s --check-prefix CHECK --check-prefix CAPTURE<br>
+# RUN: env FOO=BAR %lldb --replay %t.repro | FileCheck %s --check-prefix CHECK --check-prefix REPLAY<br>
+<br>
+# CHECK: Process [[TARGET0:[0-9]+]] stopped<br>
+# CHECK: stop reason = breakpoint 1.1<br>
+# CHECK: simple.c:12:5<br>
+# CHECK: Process [[TARGET1:[0-9]+]] stopped<br>
+# CHECK: stop reason = breakpoint 1.1<br>
+# CHECK: simple.c:16:5<br>
+# CHECK: Process [[TARGET0]] resuming<br>
+# CHECK: Process [[TARGET0]] exited<br>
+# CHECK: Process [[TARGET1]] resuming<br>
+# CHECK: Process [[TARGET1]] exited<br>
+<br>
+# CAPTURE: Reproducer is in capture mode.<br>
+# CAPTURE: Reproducer written<br>
<br>
<br>
<br>
_______________________________________________<br>
lldb-commits mailing list<br>
<a href="mailto:lldb-commits@lists.llvm.org" target="_blank">lldb-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits</a><br>
</blockquote></div>
</blockquote></div>