Index: lldb/include/lldb/Utility/Reproducer.h =================================================================== --- lldb/include/lldb/Utility/Reproducer.h +++ lldb/include/lldb/Utility/Reproducer.h @@ -309,12 +309,17 @@ const FileSpec &GetRoot() const { return m_root; } + void SetPassiveReplay(bool b) { m_passive_replay = b; } + + bool IsPassiveReplay() const { return m_passive_replay; } + private: bool HasFile(llvm::StringRef file); FileSpec m_root; std::vector m_files; bool m_loaded; + bool m_passive_replay; }; /// The reproducer enables clients to obtain access to the Generator and Index: lldb/include/lldb/Utility/ReproducerInstrumentation.h =================================================================== --- lldb/include/lldb/Utility/ReproducerInstrumentation.h +++ lldb/include/lldb/Utility/ReproducerInstrumentation.h @@ -75,66 +75,62 @@ // #define LLDB_REPRO_INSTR_TRACE #define LLDB_REGISTER_CONSTRUCTOR(Class, Signature) \ - R.Register(&construct::doit, "", #Class, \ - #Class, #Signature) + R.Register(&construct::record, "", \ + #Class, #Class, #Signature) #define LLDB_REGISTER_METHOD(Result, Class, Method, Signature) \ R.Register( \ - &invoke::method<(&Class::Method)>::doit, \ + &invoke::method<(&Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature) \ R.Register(&invoke::method_const<( \ - &Class::Method)>::doit, \ + &Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ - R.Register( \ - static_cast(&Class::Method), #Result, #Class, \ - #Method, #Signature) + R.Register( \ + &invoke::method_static<(&Class::Method)>::record, \ + #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(Result, Class, Method) \ { \ - static auto _redirect = [](char *s, size_t l) -> Result { \ - return char_ptr_redirect_static(Class::Method, s, l); \ - }; \ - R.Register( \ - static_cast(&Class::Method), _redirect, \ - #Result, #Class, #Method, "(char*, size_t"); \ + R.Register(&invoke::method_static<( \ + &Class::Method)>::record, \ + &char_ptr_redirect::method_static<( \ + &Class::Method)>::record, \ + #Result, #Class, #Method, "(char*, size_t"); \ } #define LLDB_REGISTER_CHAR_PTR_REDIRECT(Result, Class, Method) \ { \ R.Register(&invoke::method<( \ - &Class::Method)>::doit, \ + &Class::Method)>::record, \ &char_ptr_redirect::method<( \ - &Class::Method)>::doit, \ + &Class::Method)>::record, \ #Result, #Class, #Method, "(char*, size_t"); \ } #define LLDB_REGISTER_CHAR_PTR_REDIRECT_CONST(Result, Class, Method) \ { \ R.Register( \ &invoke::method_const<(&Class::Method)>::doit, \ + const>::method_const<(&Class::Method)>::record, \ &char_ptr_redirect::method_const<(&Class::Method)>::doit, \ + const>::method_const<(&Class::Method)>::record, \ #Result, #Class, #Method, "(char*, size_t"); \ } #define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ - lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ - stringify_args(__VA_ARGS__)); \ - if (lldb_private::repro::InstrumentationData _data = \ - LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::construct::doit, \ - __VA_ARGS__); \ - _recorder.RecordResult(this, false); \ + { \ + lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + lldb_private::repro::construct::handle( \ + LLDB_GET_INSTRUMENTATION_DATA(), _recorder, LLVM_PRETTY_FUNCTION, \ + this, __VA_ARGS__); \ } #define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \ - lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ - if (lldb_private::repro::InstrumentationData _data = \ - LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::construct::doit); \ - _recorder.RecordResult(this, false); \ + { \ + lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ + lldb_private::repro::construct::handle( \ + LLDB_GET_INSTRUMENTATION_DATA(), _recorder, LLVM_PRETTY_FUNCTION, \ + this); \ } #define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ @@ -142,10 +138,22 @@ stringify_args(*this, __VA_ARGS__)); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::invoke::method<(&Class::Method)>::doit, \ - this, __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke::method<( \ + &Class::Method)>::record, \ + this, __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke:: \ + method<&Class::Method>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \ @@ -153,11 +161,22 @@ stringify_args(*this, __VA_ARGS__)); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record( \ - _data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::invoke::method_const<(&Class::Method)>::doit, \ - this, __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke:: \ + method_const<(&Class::Method)>::record, \ + this, __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke:: \ + method_const<&Class::Method>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \ @@ -165,10 +184,21 @@ stringify_args(*this)); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::invoke::method<(&Class::Method)>::doit, \ - this); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record(*_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke::method<(&Class::Method)>::record, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke::method< \ + &Class::Method>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \ @@ -176,11 +206,22 @@ stringify_args(*this)); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record( \ - _data.GetSerializer(), _data.GetRegistry(), \ - &lldb_private::repro::invoke::method_const<(&Class::Method)>::doit, \ - this); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke::method_const<(&Class::Method)>::record, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke:: \ + method_const<(&Class::Method)>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \ @@ -188,17 +229,43 @@ stringify_args(__VA_ARGS__)); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - static_cast(&Class::Method), \ - __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + lldb_private::repro::invoke::method_static<( \ + &Class::Method)>::record, \ + __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke:: \ + method_static<(&Class::Method)>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method) \ lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ if (lldb_private::repro::InstrumentationData _data = \ LLDB_GET_INSTRUMENTATION_DATA()) { \ - _recorder.Record(_data.GetSerializer(), _data.GetRegistry(), \ - static_cast(&Class::Method)); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + lldb_private::repro::invoke::method_static<( \ + &Class::Method)>::record); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke::method_static<( \ + &Class::Method)>::replay(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_RESULT(Result) _recorder.RecordResult(Result, true); @@ -242,17 +309,19 @@ } /// Adds a pointer to an object to the mapping for the given index. - template void AddObjectForIndex(unsigned idx, T *object) { + template T *AddObjectForIndex(unsigned idx, T *object) { AddObjectForIndexImpl( idx, static_cast( const_cast::type *>(object))); + return object; } /// Adds a reference to an object to the mapping for the given index. - template void AddObjectForIndex(unsigned idx, T &object) { + template T &AddObjectForIndex(unsigned idx, T &object) { AddObjectForIndexImpl( idx, static_cast( const_cast::type *>(&object))); + return object; } private: @@ -321,20 +390,20 @@ } /// Store the returned value in the index-to-object mapping. - template void HandleReplayResult(const T &t) { + template T &HandleReplayResult(const T &t) { unsigned result = Deserialize(); if (is_trivially_serializable::value) - return; + return const_cast(t); // We need to make a copy as the original object might go out of scope. - m_index_to_object.AddObjectForIndex(result, new T(t)); + return *m_index_to_object.AddObjectForIndex(result, new T(t)); } /// Store the returned value in the index-to-object mapping. - template void HandleReplayResult(T *t) { + template T *HandleReplayResult(T *t) { unsigned result = Deserialize(); if (is_trivially_serializable::value) - return; - m_index_to_object.AddObjectForIndex(result, t); + return t; + return m_index_to_object.AddObjectForIndex(result, t); } /// All returned types are recorded, even when the function returns a void. @@ -442,7 +511,11 @@ DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {} void operator()(Deserializer &deserializer) const override { - deserializer.HandleReplayResult( + Replay(deserializer); + } + + Result Replay(Deserializer &deserializer) const { + return deserializer.HandleReplayResult( DeserializationHelper::template deserialized::doit( deserializer, f)); } @@ -457,6 +530,10 @@ DefaultReplayer(void (*f)(Args...)) : Replayer(), f(f) {} void operator()(Deserializer &deserializer) const override { + Replay(deserializer); + } + + void Replay(Deserializer &deserializer) const { DeserializationHelper::template deserialized::doit( deserializer, f); deserializer.HandleReplayResultVoid(); @@ -515,15 +592,19 @@ /// Returns the ID for a given function address. unsigned GetID(uintptr_t addr); + /// Get the replayer matching the given ID. + Replayer *GetReplayer(unsigned id); + + std::string GetSignature(unsigned id); + + void CheckID(unsigned expected, unsigned actual); + protected: /// Register the given replayer for a function (and the ID mapping). void DoRegister(uintptr_t RunID, std::unique_ptr replayer, SignatureStr signature); private: - std::string GetSignature(unsigned id); - Replayer *GetReplayer(unsigned id); - /// Mapping of function addresses to replayers and their ID. std::map, unsigned>> m_replayers; @@ -532,37 +613,6 @@ std::map> m_ids; }; -/// To be used as the "Runtime ID" of a constructor. It also invokes the -/// constructor when called. -template struct construct; -template struct construct { - static Class *doit(Args... args) { return new Class(args...); } -}; - -/// To be used as the "Runtime ID" of a member function. It also invokes the -/// member function when called. -template struct invoke; -template -struct invoke { - template struct method { - static Result doit(Class *c, Args... args) { return (c->*m)(args...); } - }; -}; - -template -struct invoke { - template struct method_const { - static Result doit(Class *c, Args... args) { return (c->*m)(args...); } - }; -}; - -template -struct invoke { - template struct method { - static void doit(Class *c, Args... args) { (c->*m)(args...); } - }; -}; - /// Maps an object to an index for serialization. Indices are unique and /// incremented for every new object. /// @@ -675,17 +725,27 @@ class InstrumentationData { public: - InstrumentationData() : m_serializer(nullptr), m_registry(nullptr){}; + InstrumentationData() + : m_serializer(nullptr), m_deserializer(nullptr), m_registry(nullptr){}; InstrumentationData(Serializer &serializer, Registry ®istry) - : m_serializer(&serializer), m_registry(®istry){}; - - Serializer &GetSerializer() { return *m_serializer; } + : m_serializer(&serializer), m_deserializer(nullptr), + m_registry(®istry){}; + InstrumentationData(Deserializer &deserializer, Registry ®istry) + : m_serializer(nullptr), m_deserializer(&deserializer), + m_registry(®istry){}; + + Serializer *GetSerializer() { return m_serializer; } + Deserializer *GetDeserializer() { return m_deserializer; } Registry &GetRegistry() { return *m_registry; } - operator bool() { return m_serializer != nullptr && m_registry != nullptr; } + operator bool() { + return (m_serializer != nullptr || m_deserializer != nullptr) && + m_registry != nullptr; + } private: Serializer *m_serializer; + Deserializer *m_deserializer; Registry *m_registry; }; @@ -773,14 +833,22 @@ return std::forward(r); } + template + Result ReplayResult(Result &&r, bool update_boundary) { + if (update_boundary) + UpdateBoundary(); + return std::forward(r); + } + + bool ShouldCapture() { return m_local_boundary; } + private: + template friend struct replay; void UpdateBoundary() { if (m_local_boundary) g_global_boundary = false; } - bool ShouldCapture() { return m_local_boundary; } - #ifdef LLDB_REPRO_INSTR_TRACE void Log(unsigned id) { llvm::errs() << "Recording " << id << ": " << m_pretty_func << " (" @@ -804,11 +872,157 @@ static bool g_global_boundary; }; +/// To be used as the "Runtime ID" of a constructor. It also invokes the +/// constructor when called. +template struct construct; +template struct construct { + static Class *handle(lldb_private::repro::InstrumentationData data, + lldb_private::repro::Recorder &recorder, + llvm::StringRef signature, Class *c, Args... args) { + if (!data) + return nullptr; + + if (Serializer *serializer = data.GetSerializer()) { + recorder.Record(*serializer, data.GetRegistry(), + &lldb_private::repro::construct::record, + args...); + recorder.RecordResult(c, false); + } else if (Deserializer *deserializer = data.GetDeserializer()) { + if (recorder.ShouldCapture()) { + lldb_private::repro::construct::replay( + recorder, *deserializer, data.GetRegistry(), signature); + } + } + + return nullptr; + } + + static Class *record(Args... args) { return new Class(args...); } + + static Class *replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + false); + } +}; + +/// To be used as the "Runtime ID" of a member function. It also invokes the +/// member function when called. +template struct invoke; +template +struct invoke { + template struct method { + static Result record(Class *c, Args... args) { return (c->*m)(args...); } + + static Result replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template +struct invoke { + template struct method { + static void record(Class *c, Args... args) { (c->*m)(args...); } + static void replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template +struct invoke { + template struct method_const { + static Result record(Class *c, Args... args) { return (c->*m)(args...); } + static Result replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template +struct invoke { + template struct method_const { + static void record(Class *c, Args... args) { return (c->*m)(args...); } + static void replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template struct replay; + +template +struct replay { + template struct method {}; +}; + +template +struct invoke { + template struct method_static { + static Result record(Args... args) { return (*m)(args...); } + static Result replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template struct invoke { + template struct method_static { + static void record(Args... args) { return (*m)(args...); } + static void replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned actual_id = registry.GetID(uintptr_t(&record)); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + template struct char_ptr_redirect; template struct char_ptr_redirect { template struct method_const { - static Result doit(Class *c, char *s, size_t l) { + static Result record(Class *c, char *s, size_t l) { char *buffer = reinterpret_cast(calloc(l, sizeof(char))); return (c->*m)(buffer, l); } @@ -817,19 +1031,21 @@ template struct char_ptr_redirect { template struct method { - static Result doit(Class *c, char *s, size_t l) { + static Result record(Class *c, char *s, size_t l) { char *buffer = reinterpret_cast(calloc(l, sizeof(char))); return (c->*m)(buffer, l); } }; }; - template -Result char_ptr_redirect_static(Result (*f)(char *, size_t), char *s, - size_t l) { - char *buffer = reinterpret_cast(calloc(l, sizeof(char))); - return f(buffer, l); -} +struct char_ptr_redirect { + template struct method_static { + static Result record(char *s, size_t l) { + char *buffer = reinterpret_cast(calloc(l, sizeof(char))); + return (*m)(buffer, l); + } + }; +}; } // namespace repro } // namespace lldb_private Index: lldb/source/API/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ lldb/source/API/SBDebugger.cpp @@ -1629,31 +1629,31 @@ template <> void RegisterMethods(Registry &R) { // Custom implementation. - R.Register(&invoke::method<&SBDebugger::SetErrorFileHandle>::doit, + R.Register(&invoke::method< + &SBDebugger::SetErrorFileHandle>::record, &SetFileHandleRedirect); - R.Register(&invoke::method<&SBDebugger::SetOutputFileHandle>::doit, + R.Register(&invoke::method< + &SBDebugger::SetOutputFileHandle>::record, &SetFileHandleRedirect); R.Register(&invoke::method<&SBDebugger::SetInputFile>::doit, + SBFile)>::method<&SBDebugger::SetInputFile>::record, &SetFileRedirect); R.Register(&invoke::method<&SBDebugger::SetOutputFile>::doit, + SBFile)>::method<&SBDebugger::SetOutputFile>::record, &SetFileRedirect); R.Register(&invoke::method<&SBDebugger::SetErrorFile>::doit, + SBFile)>::method<&SBDebugger::SetErrorFile>::record, &SetFileRedirect); R.Register(&invoke::method<&SBDebugger::SetInputFile>::doit, + FileSP)>::method<&SBDebugger::SetInputFile>::record, &SetFileRedirect); R.Register(&invoke::method<&SBDebugger::SetOutputFile>::doit, + FileSP)>::method<&SBDebugger::SetOutputFile>::record, &SetFileRedirect); R.Register(&invoke::method<&SBDebugger::SetErrorFile>::doit, + FileSP)>::method<&SBDebugger::SetErrorFile>::record, &SetFileRedirect); LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(bool, SBDebugger, Index: lldb/source/API/SBReproducer.cpp =================================================================== --- lldb/source/API/SBReproducer.cpp +++ lldb/source/API/SBReproducer.cpp @@ -130,6 +130,8 @@ error = llvm::toString(std::move(e)); return error.c_str(); } + repro::Loader *loader = repro::Reproducer::Instance().GetLoader(); + loader->SetAPIReplay(true); return nullptr; } Index: lldb/source/API/SBReproducerPrivate.h =================================================================== --- lldb/source/API/SBReproducerPrivate.h +++ lldb/source/API/SBReproducerPrivate.h @@ -55,6 +55,17 @@ SBRegistry m_registry; }; +class ReplayData { +public: + ReplayData(llvm::StringRef buffer) : m_registry(), m_deserializer(buffer) {} + Deserializer &GetDeserializer() { return m_deserializer; } + Registry &GetRegistry() { return m_registry; } + +private: + SBRegistry m_registry; + Deserializer m_deserializer; +}; + inline InstrumentationData GetInstrumentationData() { if (!lldb_private::repro::Reproducer::Initialized()) return {}; @@ -64,6 +75,17 @@ return {p.GetSerializer(), p.GetRegistry()}; } + if (auto *l = lldb_private::repro::Reproducer::Instance().GetLoader()) { + if (l->IsPassiveReplay()) { + FileSpec file = l->GetFile(); + static auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (!error_or_file) + return {}; + static ReplayData r((*error_or_file)->getBuffer()); + return {r.GetDeserializer(), r.GetRegistry()}; + } + } + return {}; } Index: lldb/source/Utility/Reproducer.cpp =================================================================== --- lldb/source/Utility/Reproducer.cpp +++ lldb/source/Utility/Reproducer.cpp @@ -228,7 +228,8 @@ } Loader::Loader(FileSpec root) - : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {} + : m_root(MakeAbsolute(std::move(root))), m_loaded(false), + m_passive_replay(false) {} llvm::Error Loader::LoadIndex() { if (m_loaded) Index: lldb/source/Utility/ReproducerInstrumentation.cpp =================================================================== --- lldb/source/Utility/ReproducerInstrumentation.cpp +++ lldb/source/Utility/ReproducerInstrumentation.cpp @@ -8,9 +8,9 @@ #include "lldb/Utility/ReproducerInstrumentation.h" #include "lldb/Utility/Reproducer.h" -#include #include #include +#include using namespace lldb_private; using namespace lldb_private::repro; @@ -97,7 +97,7 @@ // Add a small artificial delay to ensure that all asynchronous events have // completed before we exit. - std::this_thread::sleep_for (std::chrono::milliseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); return true; } @@ -122,6 +122,22 @@ return m_ids[id].second.ToString(); } +void Registry::CheckID(unsigned expected, unsigned actual) { + if (expected != actual) { + llvm::errs() << "Reproducer expected signature " << expected << ": '" + << GetSignature(expected) << "'\n"; + llvm::errs() << "Reproducer actual signature " << actual << ": '" + << GetSignature(actual) << "'\n"; + llvm::report_fatal_error( + "Detected reproducer replay divergence. Refusing to continue."); + } + +#ifdef LLDB_REPRO_INSTR_TRACE + llvm::errs() << "Replaying " << actual << ": " << GetSignature(actual) + << "\n"; +#endif +} + Replayer *Registry::GetReplayer(unsigned id) { assert(m_ids.count(id) != 0 && "ID not in registry"); return m_ids[id].first;