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 SetAPIReplay(bool b) { m_api_replay = b; } + + bool IsAPIReplay() const { return m_api_replay; } + private: bool HasFile(llvm::StringRef file); FileSpec m_root; std::vector m_files; bool m_loaded; + bool m_api_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 @@ -122,19 +122,39 @@ 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); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record(*_serializer, _data.GetRegistry(), \ + &lldb_private::repro::construct::doit, \ + __VA_ARGS__); \ + _recorder.RecordResult(this, false); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + lldb_private::repro::replay_construct::doit( \ + _recorder, *_deserializer, _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #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); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record(*_serializer, _data.GetRegistry(), \ + &lldb_private::repro::construct::doit); \ + _recorder.RecordResult(this, false); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + lldb_private::repro::replay_construct::doit( \ + _recorder, *_deserializer, _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ @@ -142,10 +162,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)>::doit, \ + this, __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay:: \ + method<&Class::Method>::doit(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \ @@ -153,11 +185,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)>::doit, \ + this, __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay:: \ + method_const<&Class::Method>::doit(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \ @@ -165,10 +208,20 @@ 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)>::doit, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay::method< \ + &Class::Method>::doit(_recorder, *_deserializer, \ + _data.GetRegistry(), LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \ @@ -176,11 +229,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)>::doit, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay:: \ + method_const<(&Class::Method)>::doit(_recorder, *_deserializer, \ + _data.GetRegistry(), \ + LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \ @@ -188,17 +252,38 @@ 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(), \ + static_cast(&Class::Method), \ + __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay:: \ + method_static<(&Class::Method)>::doit(_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(), \ + static_cast(&Class::Method)); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::replay::method_static<( \ + &Class::Method)>::doit(_recorder, *_deserializer, \ + _data.GetRegistry(), LLVM_PRETTY_FUNCTION); \ + } \ + } \ } #define LLDB_RECORD_RESULT(Result) _recorder.RecordResult(Result, true); @@ -242,17 +327,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 +408,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 +529,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 +548,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 +610,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 CheckSignature(llvm::StringRef signature, unsigned id); + 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; @@ -675,17 +774,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 +882,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,6 +921,107 @@ static bool g_global_boundary; }; +template struct replay_construct; +template +struct replay_construct { + static Class *doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + false); + } +}; + +template struct replay; + +template +struct replay { + template struct method { + static Result doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template +struct replay { + template struct method_const { + static Result doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template +struct replay { + template struct method_static { + static Result doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + return recorder.ReplayResult( + static_cast *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template +struct replay { + template struct method { + static void doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template +struct replay { + template struct method_const { + static void doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template struct replay { + template struct method_static { + static void doit(Recorder &recorder, Deserializer &deserializer, + Registry ®istry, llvm::StringRef signature) { + unsigned id = deserializer.Deserialize(); + registry.CheckSignature(signature, id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + template struct char_ptr_redirect; template struct char_ptr_redirect { 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->IsAPIReplay()) { + 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_api_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 @@ -117,6 +117,39 @@ return m_ids[id].second.ToString(); } +/// Extract the method name from the given signature. +static llvm::StringRef GetMethodName(llvm::StringRef signature) { + auto pos = signature.find('('); + if (pos == llvm::StringRef::npos) + return {}; + + llvm::StringRef temp = signature.substr(0, pos); + pos = temp.rfind("::"); + if (pos == llvm::StringRef::npos) + return {}; + + return temp.substr(pos); +} + +void Registry::CheckSignature(llvm::StringRef expected, unsigned id) { + std::string actual = GetSignature(id); + + llvm::StringRef expected_method = GetMethodName(expected); + llvm::StringRef actual_method = GetMethodName(actual); + + /// This check is an approximation but will catch divergences eventually. + if (expected_method.empty() || actual_method.empty() || + expected_method != actual_method) { + llvm::errs() << "Reproducer expected signature: '" << expected << "'\n"; + llvm::errs() << "Reproducer actual signature: '" << actual << "'\n"; + llvm::report_fatal_error( + "Detected reproducer replay divergence. Refusing to continue."); + } +#ifdef LLDB_REPRO_INSTR_TRACE + llvm::errs() << "Replaying " << id << ": " << actual << "\n"; +#endif +} + Replayer *Registry::GetReplayer(unsigned id) { assert(m_ids.count(id) != 0 && "ID not in registry"); return m_ids[id].first;