Index: lldb/include/lldb/Utility/GDBRemote.h =================================================================== --- lldb/include/lldb/Utility/GDBRemote.h +++ lldb/include/lldb/Utility/GDBRemote.h @@ -9,6 +9,8 @@ #ifndef liblldb_GDBRemote_h_ #define liblldb_GDBRemote_h_ +#include "lldb/Utility/FileSpec.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/StreamString.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-public.h" @@ -69,7 +71,6 @@ std::string data; }; - void Serialize(llvm::raw_ostream &strm) const; void Dump(Stream &strm) const; BinaryData packet; @@ -82,6 +83,46 @@ llvm::StringRef GetTypeStr() const; }; +namespace repro { +class PacketRecorder : public AbstractRecorder { +public: + PacketRecorder(const FileSpec &filename, std::error_code &ec) + : AbstractRecorder(filename, ec) {} + + static llvm::Expected> + Create(const FileSpec &filename); + + void Record(const GDBRemotePacket &packet); +}; + +class GDBRemoteProvider : public repro::Provider { +public: + struct Info { + static const char *name; + static const char *file; + }; + + GDBRemoteProvider(const FileSpec &directory) : Provider(directory) {} + + llvm::raw_ostream *GetHistoryStream(); + PacketRecorder *GetNewPacketRecorder(); + + void SetCallback(std::function callback) { + m_callback = std::move(callback); + } + + void Keep() override; + void Discard() override; + + static char ID; + +private: + std::function m_callback; + std::unique_ptr m_stream_up; + std::vector> m_packet_recorders; +}; + +} // namespace repro } // namespace lldb_private LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::GDBRemotePacket) Index: lldb/include/lldb/Utility/Reproducer.h =================================================================== --- lldb/include/lldb/Utility/Reproducer.h +++ lldb/include/lldb/Utility/Reproducer.h @@ -20,6 +20,7 @@ #include namespace lldb_private { +struct GDBRemotePacket; namespace repro { class Reproducer; @@ -153,24 +154,13 @@ static char ID; }; -class DataRecorder { -public: - DataRecorder(const FileSpec &filename, std::error_code &ec) +class AbstractRecorder { +protected: + AbstractRecorder(const FileSpec &filename, std::error_code &ec) : m_filename(filename.GetFilename().GetStringRef()), m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {} - static llvm::Expected> - Create(const FileSpec &filename); - - template void Record(const T &t, bool newline = false) { - if (!m_record) - return; - m_os << t; - if (newline) - m_os << '\n'; - m_os.flush(); - } - +public: const FileSpec &GetFilename() { return m_filename; } void Stop() { @@ -180,10 +170,30 @@ private: FileSpec m_filename; + +protected: llvm::raw_fd_ostream m_os; bool m_record; }; +class DataRecorder : public AbstractRecorder { +public: + DataRecorder(const FileSpec &filename, std::error_code &ec) + : AbstractRecorder(filename, ec) {} + + static llvm::Expected> + Create(const FileSpec &filename); + + template void Record(const T &t, bool newline = false) { + if (!m_record) + return; + m_os << t; + if (newline) + m_os << '\n'; + m_os.flush(); + } +}; + class CommandProvider : public Provider { public: struct Info { @@ -204,32 +214,6 @@ std::vector> m_data_recorders; }; -class ProcessGDBRemoteProvider - : public repro::Provider { -public: - struct Info { - static const char *name; - static const char *file; - }; - - ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {} - - llvm::raw_ostream *GetHistoryStream(); - - void SetCallback(std::function callback) { - m_callback = std::move(callback); - } - - void Keep() override { m_callback(); } - void Discard() override { m_callback(); } - - static char ID; - -private: - std::function m_callback; - std::unique_ptr m_stream_up; -}; - /// 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. @@ -359,13 +343,43 @@ mutable std::mutex m_mutex; }; -/// Helper class for replaying commands through the reproducer. -class CommandLoader { +template class MultiLoader { public: - CommandLoader(std::vector files) : m_files(files) {} + MultiLoader(std::vector files) : m_files(files) {} + + static std::unique_ptr Create(Loader *loader) { + if (!loader) + return {}; + + FileSpec file = loader->GetFile(); + if (!file) + return {}; + + auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (auto err = error_or_file.getError()) + return {}; - static std::unique_ptr Create(Loader *loader); - llvm::Optional GetNextFile(); + std::vector files; + llvm::yaml::Input yin((*error_or_file)->getBuffer()); + yin >> files; + + if (auto err = yin.error()) + return {}; + + for (auto &file : files) { + FileSpec absolute_path = + loader->GetRoot().CopyByAppendingPathComponent(file); + file = absolute_path.GetPath(); + } + + return std::make_unique>(std::move(files)); + } + + llvm::Optional GetNextFile() { + if (m_index >= m_files.size()) + return {}; + return m_files[m_index++]; + } private: std::vector m_files; Index: lldb/source/API/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ lldb/source/API/SBDebugger.cpp @@ -315,8 +315,9 @@ FileSP file_sp = file.m_opaque_sp; - static std::unique_ptr loader = - repro::CommandLoader::Create(repro::Reproducer::Instance().GetLoader()); + static std::unique_ptr> loader = + repro::MultiLoader::Create( + repro::Reproducer::Instance().GetLoader()); if (loader) { llvm::Optional nextfile = loader->GetNextFile(); FILE *fh = nextfile ? FileSystem::Instance().Fopen(nextfile->c_str(), "r") Index: lldb/source/Commands/CommandObjectReproducer.cpp =================================================================== --- lldb/source/Commands/CommandObjectReproducer.cpp +++ lldb/source/Commands/CommandObjectReproducer.cpp @@ -408,9 +408,9 @@ } case eReproducerProviderCommands: { // Create a new command loader. - std::unique_ptr command_loader = - repro::CommandLoader::Create(loader); - if (!command_loader) { + std::unique_ptr> multi_loader = + repro::MultiLoader::Create(loader); + if (!multi_loader) { SetError(result, make_error(llvm::inconvertibleErrorCode(), "Unable to create command loader.")); @@ -418,9 +418,8 @@ } // Iterate over the command files and dump them. - while (true) { - llvm::Optional command_file = - command_loader->GetNextFile(); + llvm::Optional command_file; + while ((command_file = multi_loader->GetNextFile())) { if (!command_file) break; @@ -436,24 +435,30 @@ return true; } case eReproducerProviderGDB: { - FileSpec gdb_file = loader->GetFile(); - auto error_or_file = MemoryBuffer::getFile(gdb_file.GetPath()); - if (auto err = error_or_file.getError()) { - SetError(result, errorCodeToError(err)); - return false; - } - std::vector packets; - yaml::Input yin((*error_or_file)->getBuffer()); - yin >> packets; + static std::unique_ptr> + multi_loader = + repro::MultiLoader::Create(loader); + llvm::Optional gdb_file; + while ((gdb_file = multi_loader->GetNextFile())) { + auto error_or_file = MemoryBuffer::getFile(*gdb_file); + if (auto err = error_or_file.getError()) { + SetError(result, errorCodeToError(err)); + return false; + } - if (auto err = yin.error()) { - SetError(result, errorCodeToError(err)); - return false; - } + std::vector packets; + yaml::Input yin((*error_or_file)->getBuffer()); + yin >> packets; + + if (auto err = yin.error()) { + SetError(result, errorCodeToError(err)); + return false; + } - for (GDBRemotePacket &packet : packets) { - packet.Dump(result.GetOutputStream()); + for (GDBRemotePacket &packet : packets) { + packet.Dump(result.GetOutputStream()); + } } result.SetStatus(eReturnStatusSuccessFinishResult); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h @@ -27,6 +27,9 @@ #include "lldb/lldb-public.h" namespace lldb_private { +namespace repro { +class PacketRecorder; +} namespace process_gdb_remote { enum GDBStoppointType { @@ -133,7 +136,8 @@ // fork/exec to avoid having to connect/accept void DumpHistory(Stream &strm); - void SetHistoryStream(llvm::raw_ostream *strm); + + void SetPacketRecorder(repro::PacketRecorder *recorder); static llvm::Error ConnectLocally(GDBRemoteCommunication &client, GDBRemoteCommunication &server); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp @@ -31,6 +31,7 @@ #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/Log.h" #include "lldb/Utility/RegularExpression.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/Utility/StreamString.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/ScopedPrinter.h" @@ -1243,8 +1244,9 @@ void GDBRemoteCommunication::DumpHistory(Stream &strm) { m_history.Dump(strm); } -void GDBRemoteCommunication::SetHistoryStream(llvm::raw_ostream *strm) { - m_history.SetStream(strm); +void GDBRemoteCommunication::SetPacketRecorder( + repro::PacketRecorder *recorder) { + m_history.SetRecorder(recorder); } llvm::Error Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.h @@ -13,11 +13,15 @@ #include #include "lldb/Utility/GDBRemote.h" +#include "lldb/Utility/Reproducer.h" #include "lldb/lldb-public.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" namespace lldb_private { +namespace repro { +class PacketRecorder; +} namespace process_gdb_remote { /// The history keeps a circular buffer of GDB remote packets. The history is @@ -41,7 +45,7 @@ void Dump(Log *log) const; bool DidDumpToLog() const { return m_dumped_to_log; } - void SetStream(llvm::raw_ostream *strm) { m_stream = strm; } + void SetRecorder(repro::PacketRecorder *recorder) { m_recorder = recorder; } private: uint32_t GetFirstSavedPacketIndex() const { @@ -73,7 +77,7 @@ uint32_t m_curr_idx; uint32_t m_total_packet_count; mutable bool m_dumped_to_log; - llvm::raw_ostream *m_stream = nullptr; + repro::PacketRecorder *m_recorder = nullptr; }; } // namespace process_gdb_remote Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationHistory.cpp @@ -40,8 +40,8 @@ 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) - m_packets[idx].Serialize(*m_stream); + if (m_recorder) + m_recorder->Record(m_packets[idx]); } void GDBRemoteCommunicationHistory::AddPacket(const std::string &src, @@ -58,8 +58,8 @@ 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) - m_packets[idx].Serialize(*m_stream); + if (m_recorder) + m_recorder->Record(m_packets[idx]); } void GDBRemoteCommunicationHistory::Dump(Stream &strm) const { Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -279,12 +279,9 @@ "async thread did exit"); if (repro::Generator *g = repro::Reproducer::Instance().GetGenerator()) { - repro::ProcessGDBRemoteProvider &provider = - g->GetOrCreate(); - // 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); }); + repro::GDBRemoteProvider &provider = + g->GetOrCreate(); + m_gdb_comm.SetPacketRecorder(provider.GetNewPacketRecorder()); } Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_ASYNC)); @@ -3362,17 +3359,20 @@ if (!loader) return Status("No loader provided."); - // Construct replay history path. - FileSpec history_file = - loader->GetFile(); - if (!history_file) - return Status("No provider for gdb-remote."); + static std::unique_ptr> + multi_loader = repro::MultiLoader::Create( + repro::Reproducer::Instance().GetLoader()); - // Enable replay mode. - m_replay_mode = true; + if (!multi_loader) + return Status("No gdb remote provider found."); + + llvm::Optional history_file = multi_loader->GetNextFile(); + if (!history_file) + return Status("No gdb remote packet log found."); // Load replay history. - if (auto error = m_gdb_replay_server.LoadReplayHistory(history_file)) + if (auto error = + m_gdb_replay_server.LoadReplayHistory(FileSpec(*history_file))) return Status("Unable to load replay history"); // Make a local connection. @@ -3380,6 +3380,9 @@ m_gdb_replay_server)) return Status("Unable to connect to replay server"); + // Enable replay mode. + m_replay_mode = true; + // Start server thread. m_gdb_replay_server.StartAsyncThread(); Index: lldb/source/Utility/GDBRemote.cpp =================================================================== --- lldb/source/Utility/GDBRemote.cpp +++ lldb/source/Utility/GDBRemote.cpp @@ -14,6 +14,7 @@ #include using namespace lldb; +using namespace lldb_private::repro; using namespace lldb_private; using namespace llvm; @@ -45,12 +46,6 @@ return bytes_written; } -void GDBRemotePacket::Serialize(raw_ostream &strm) const { - yaml::Output yout(strm); - yout << const_cast(*this); - strm.flush(); -} - llvm::StringRef GDBRemotePacket::GetTypeStr() const { switch (type) { case GDBRemotePacket::ePacketTypeSend: @@ -103,3 +98,66 @@ return {}; } + +void GDBRemoteProvider::Keep() { + std::vector files; + for (auto &recorder : m_packet_recorders) { + files.push_back(recorder->GetFilename().GetPath()); + } + + FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); + std::error_code ec; + llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); + if (ec) + return; + yaml::Output yout(os); + yout << files; +} + +void GDBRemoteProvider::Discard() { m_packet_recorders.clear(); } + +llvm::Expected> +PacketRecorder::Create(const FileSpec &filename) { + std::error_code ec; + auto recorder = std::make_unique(std::move(filename), ec); + if (ec) + return llvm::errorCodeToError(ec); + return std::move(recorder); +} + +PacketRecorder *GDBRemoteProvider::GetNewPacketRecorder() { + std::size_t i = m_packet_recorders.size() + 1; + std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + + llvm::Twine(i) + llvm::Twine(".yaml")) + .str(); + auto recorder_or_error = + PacketRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); + if (!recorder_or_error) { + llvm::consumeError(recorder_or_error.takeError()); + return nullptr; + } + + m_packet_recorders.push_back(std::move(*recorder_or_error)); + return m_packet_recorders.back().get(); +} + +void PacketRecorder::Record(const GDBRemotePacket &packet) { + if (!m_record) + return; + yaml::Output yout(m_os); + yout << const_cast(packet); + m_os.flush(); +} + +llvm::raw_ostream *GDBRemoteProvider::GetHistoryStream() { + FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file); + + std::error_code EC; + m_stream_up = std::make_unique(history_file.GetPath(), EC, + sys::fs::OpenFlags::OF_Text); + return m_stream_up.get(); +} + +char GDBRemoteProvider::ID = 0; +const char *GDBRemoteProvider::Info::file = "gdb-remote.yaml"; +const char *GDBRemoteProvider::Info::name = "gdb-remote"; Index: lldb/source/Utility/Reproducer.cpp =================================================================== --- lldb/source/Utility/Reproducer.cpp +++ lldb/source/Utility/Reproducer.cpp @@ -255,7 +255,7 @@ DataRecorder *CommandProvider::GetNewDataRecorder() { std::size_t i = m_data_recorders.size() + 1; std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + - llvm::Twine(i) + llvm::Twine(".txt")) + llvm::Twine(i) + llvm::Twine(".yaml")) .str(); auto recorder_or_error = DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); @@ -304,53 +304,9 @@ os << m_cwd << "\n"; } -llvm::raw_ostream *ProcessGDBRemoteProvider::GetHistoryStream() { - FileSpec history_file = GetRoot().CopyByAppendingPathComponent(Info::file); - - std::error_code EC; - m_stream_up = std::make_unique(history_file.GetPath(), EC, - sys::fs::OpenFlags::OF_Text); - return m_stream_up.get(); -} - -std::unique_ptr CommandLoader::Create(Loader *loader) { - if (!loader) - return {}; - - FileSpec file = loader->GetFile(); - if (!file) - return {}; - - auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); - if (auto err = error_or_file.getError()) - return {}; - - std::vector files; - llvm::yaml::Input yin((*error_or_file)->getBuffer()); - yin >> files; - - if (auto err = yin.error()) - return {}; - - for (auto &file : files) { - FileSpec absolute_path = - loader->GetRoot().CopyByAppendingPathComponent(file); - file = absolute_path.GetPath(); - } - - return std::make_unique(std::move(files)); -} - -llvm::Optional CommandLoader::GetNextFile() { - if (m_index >= m_files.size()) - return {}; - return m_files[m_index++]; -} - void ProviderBase::anchor() {} char CommandProvider::ID = 0; char FileProvider::ID = 0; -char ProcessGDBRemoteProvider::ID = 0; char ProviderBase::ID = 0; char VersionProvider::ID = 0; char WorkingDirectoryProvider::ID = 0; @@ -358,8 +314,6 @@ const char *CommandProvider::Info::name = "command-interpreter"; const char *FileProvider::Info::file = "files.yaml"; const char *FileProvider::Info::name = "files"; -const char *ProcessGDBRemoteProvider::Info::file = "gdb-remote.yaml"; -const char *ProcessGDBRemoteProvider::Info::name = "gdb-remote"; const char *VersionProvider::Info::file = "version.txt"; const char *VersionProvider::Info::name = "version"; const char *WorkingDirectoryProvider::Info::file = "cwd.txt";