diff --git a/lldb/include/lldb/Utility/Reproducer.h b/lldb/include/lldb/Utility/Reproducer.h --- a/lldb/include/lldb/Utility/Reproducer.h +++ b/lldb/include/lldb/Utility/Reproducer.h @@ -27,6 +27,7 @@ enum class ReproducerMode { Capture, Replay, + PassiveReplay, Off, }; @@ -287,7 +288,7 @@ class Loader final { public: - Loader(FileSpec root); + Loader(FileSpec root, bool passive = false); template FileSpec GetFile() { if (!HasFile(T::file)) @@ -309,12 +310,15 @@ const FileSpec &GetRoot() const { return m_root; } + 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 @@ -342,7 +346,7 @@ protected: llvm::Error SetCapture(llvm::Optional root); - llvm::Error SetReplay(llvm::Optional root); + llvm::Error SetReplay(llvm::Optional root, bool passive = false); private: static llvm::Optional &InstanceImpl(); diff --git a/lldb/include/lldb/Utility/ReproducerInstrumentation.h b/lldb/include/lldb/Utility/ReproducerInstrumentation.h --- a/lldb/include/lldb/Utility/ReproducerInstrumentation.h +++ b/lldb/include/lldb/Utility/ReproducerInstrumentation.h @@ -80,68 +80,72 @@ // #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<(&Class::Method)>::doit, \ + Signature const>::method<(&Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ - R.Register(&invoke::method<(&Class::Method)>::doit, \ + R.Register(&invoke::method<(&Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(Result, Class, Method) \ R.Register( \ - &invoke::method<(&Class::Method)>::doit, \ - &char_ptr_redirect::method<(&Class::Method)>::doit, \ + &invoke::method<(&Class::Method)>::record, \ + &char_ptr_redirect::method<( \ + &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<(&Class::Method)>::doit, \ + const>::method<(&Class::Method)>::record, \ &char_ptr_redirect::method<(&Class::Method)>::doit, \ + const>::method<(&Class::Method)>::record, \ #Result, #Class, #Method, "(char*, size_t"); -#define LLDB_CONSTRUCT_(T, ...) \ - lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ - stringify_args(this, __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); \ - } +#define LLDB_CONSTRUCT_(T, Class, ...) \ + lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ + lldb_private::repro::construct::handle(LLDB_GET_INSTRUMENTATION_DATA(), \ + _recorder, Class, __VA_ARGS__); #define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ - LLDB_CONSTRUCT_(Class Signature, __VA_ARGS__) + LLDB_CONSTRUCT_(Class Signature, this, __VA_ARGS__) #define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \ - LLDB_CONSTRUCT_(Class(), lldb_private::repro::EmptyArg()) + LLDB_CONSTRUCT_(Class(), this, lldb_private::repro::EmptyArg()) #define LLDB_RECORD_(T1, T2, ...) \ 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::invoke::method::doit, \ - __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record(*_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke::method::record, \ + __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke::method::replay( \ + _recorder, *_deserializer, _data.GetRegistry()); \ + } \ + } \ } #define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ @@ -171,6 +175,7 @@ #define LLDB_RECORD_DUMMY(Result, Class, Method, Signature, ...) \ lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ stringify_args(__VA_ARGS__)); + #define LLDB_RECORD_DUMMY_NO_ARGS(Result, Class, Method) \ lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); @@ -204,17 +209,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; } /// Get all objects sorted by their index. @@ -285,21 +292,29 @@ return t; } + template const T &HandleReplayResult(const T &t) { + unsigned result = Deserialize(); + if (is_trivially_serializable::value) + return t; + // We need to make a copy as the original object might go out of scope. + return *m_index_to_object.AddObjectForIndex(result, new T(t)); + } + /// Store the returned value in the index-to-object mapping. - template void HandleReplayResult(const T &t) { + template T &HandleReplayResult(T &t) { unsigned result = Deserialize(); if (is_trivially_serializable::value) - return; + return 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. @@ -411,7 +426,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)); } @@ -426,6 +445,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(); @@ -487,15 +510,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; @@ -504,50 +531,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 { - static Result doit(Class *c, Args... args) { return (c->*m)(args...); } - }; -}; - -template -struct invoke { - template struct method { - static Result doit(Args... args) { return (*m)(args...); } - }; -}; - -template struct invoke { - template struct method { - static void doit(Args... args) { return (*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. /// @@ -656,21 +639,41 @@ /// Mapping of objects to indices. ObjectToIndex m_tracker; -}; +}; // namespace repro class InstrumentationData { public: - InstrumentationData() : m_serializer(nullptr), m_registry(nullptr){}; - InstrumentationData(Serializer &serializer, Registry ®istry) - : m_serializer(&serializer), m_registry(®istry){}; - - Serializer &GetSerializer() { return *m_serializer; } + 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; + } + + static void Initialize(Serializer &serializer, Registry ®istry); + static void Initialize(Deserializer &serializer, Registry ®istry); + static InstrumentationData &Instance(); + +protected: + friend llvm::optional_detail::OptionalStorage; + friend llvm::Optional; + + InstrumentationData() + : m_serializer(nullptr), m_deserializer(nullptr), m_registry(nullptr) {} + InstrumentationData(Serializer &serializer, Registry ®istry) + : m_serializer(&serializer), m_deserializer(nullptr), + m_registry(®istry) {} + InstrumentationData(Deserializer &deserializer, Registry ®istry) + : m_serializer(nullptr), m_deserializer(&deserializer), + m_registry(®istry) {} private: + static llvm::Optional &InstanceImpl(); + Serializer *m_serializer; + Deserializer *m_deserializer; Registry *m_registry; }; @@ -769,14 +772,41 @@ return std::forward(r); } + template + Result Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr, + bool update_boundary) { + unsigned actual_id = registry.GetID(addr); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + return ReplayResult( + static_cast *>(registry.GetReplayer(id)) + ->Replay(deserializer), + update_boundary); + } + + void Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr) { + unsigned actual_id = registry.GetID(addr); + unsigned id = deserializer.Deserialize(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + + 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 << " (" @@ -800,11 +830,127 @@ 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, Class *c, + const EmptyArg &) { + return handle(data, recorder, c); + } + + static Class *handle(lldb_private::repro::InstrumentationData data, + lldb_private::repro::Recorder &recorder, Class *c, + Args... args) { + if (!data) + return nullptr; + + if (Serializer *serializer = data.GetSerializer()) { + recorder.Record(*serializer, data.GetRegistry(), &record, args...); + recorder.RecordResult(c, false); + } else if (Deserializer *deserializer = data.GetDeserializer()) { + if (recorder.ShouldCapture()) { + replay(recorder, *deserializer, data.GetRegistry()); + } + } + + return nullptr; + } + + static Class *record(Args... args) { return new Class(args...); } + + static Class *replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry) { + return recorder.Replay( + deserializer, registry, uintptr_t(&record), 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) { + return recorder.Replay( + deserializer, registry, uintptr_t(&record), 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) { + recorder.Replay(deserializer, registry, uintptr_t(&record)); + } + }; +}; + +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) { + return recorder.Replay( + deserializer, registry, uintptr_t(&record), true); + } + }; +}; + +template +struct invoke { + template struct method { + static void record(Class *c, Args... args) { return (c->*m)(args...); } + static void replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry) { + recorder.Replay(deserializer, registry, uintptr_t(&record)); + } + }; +}; + +template struct replay; + +template +struct replay { + template struct method {}; +}; + +template +struct invoke { + template struct method { + static Result record(Args... args) { return (*m)(args...); } + static Result replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry) { + return recorder.Replay(deserializer, registry, + uintptr_t(&record), true); + } + }; +}; + +template struct invoke { + template struct method { + static void record(Args... args) { return (*m)(args...); } + static void replay(Recorder &recorder, Deserializer &deserializer, + Registry ®istry) { + recorder.Replay(deserializer, registry, uintptr_t(&record)); + } + }; +}; + template struct char_ptr_redirect; 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); } @@ -813,17 +959,16 @@ 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 struct char_ptr_redirect { template struct method { - static Result doit(char *s, size_t l) { + static Result record(char *s, size_t l) { char *buffer = reinterpret_cast(calloc(l, sizeof(char))); return (*m)(buffer, l); } diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -1641,31 +1641,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, diff --git a/lldb/source/API/SBReproducer.cpp b/lldb/source/API/SBReproducer.cpp --- a/lldb/source/API/SBReproducer.cpp +++ b/lldb/source/API/SBReproducer.cpp @@ -111,6 +111,12 @@ error = llvm::toString(std::move(e)); return error.c_str(); } + + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { + auto &p = g->GetOrCreate(); + InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry()); + } + return nullptr; } @@ -121,15 +127,35 @@ error = llvm::toString(std::move(e)); return error.c_str(); } + + if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { + auto &p = g->GetOrCreate(); + InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry()); + } + return nullptr; } const char *SBReproducer::PassiveReplay(const char *path) { static std::string error; - if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) { + if (auto e = Reproducer::Initialize(ReproducerMode::PassiveReplay, + FileSpec(path))) { error = llvm::toString(std::move(e)); return error.c_str(); } + + if (auto *l = lldb_private::repro::Reproducer::Instance().GetLoader()) { + FileSpec file = l->GetFile(); + auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); + if (!error_or_file) { + error = + "unable to read SB API data: " + error_or_file.getError().message(); + return error.c_str(); + } + static ReplayData r(std::move(*error_or_file)); + InstrumentationData::Initialize(r.GetDeserializer(), r.GetRegistry()); + } + return nullptr; } diff --git a/lldb/source/API/SBReproducerPrivate.h b/lldb/source/API/SBReproducerPrivate.h --- a/lldb/source/API/SBReproducerPrivate.h +++ b/lldb/source/API/SBReproducerPrivate.h @@ -20,7 +20,7 @@ #include "llvm/ADT/DenseMap.h" #define LLDB_GET_INSTRUMENTATION_DATA() \ - lldb_private::repro::GetInstrumentationData() + lldb_private::repro::InstrumentationData::Instance() namespace lldb_private { namespace repro { @@ -55,17 +55,19 @@ SBRegistry m_registry; }; -inline InstrumentationData GetInstrumentationData() { - if (!lldb_private::repro::Reproducer::Initialized()) - return {}; - - if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) { - auto &p = g->GetOrCreate(); - return {p.GetSerializer(), p.GetRegistry()}; - } +class ReplayData { +public: + ReplayData(std::unique_ptr memory_buffer) + : m_memory_buffer(std::move(memory_buffer)), m_registry(), + m_deserializer(m_memory_buffer->getBuffer()) {} + Deserializer &GetDeserializer() { return m_deserializer; } + Registry &GetRegistry() { return m_registry; } - return {}; -} +private: + std::unique_ptr m_memory_buffer; + SBRegistry m_registry; + Deserializer m_deserializer; +}; template void RegisterMethods(Registry &R); diff --git a/lldb/source/Utility/Reproducer.cpp b/lldb/source/Utility/Reproducer.cpp --- a/lldb/source/Utility/Reproducer.cpp +++ b/lldb/source/Utility/Reproducer.cpp @@ -62,7 +62,9 @@ return Instance().SetCapture(root); } break; case ReproducerMode::Replay: - return Instance().SetReplay(root); + return Instance().SetReplay(root, /*passive*/ false); + case ReproducerMode::PassiveReplay: + return Instance().SetReplay(root, /*passive*/ true); case ReproducerMode::Off: break; }; @@ -127,7 +129,7 @@ return Error::success(); } -llvm::Error Reproducer::SetReplay(llvm::Optional root) { +llvm::Error Reproducer::SetReplay(llvm::Optional root, bool passive) { std::lock_guard guard(m_mutex); if (root && m_generator) @@ -140,7 +142,7 @@ return Error::success(); } - m_loader.emplace(*root); + m_loader.emplace(*root, passive); if (auto e = m_loader->LoadIndex()) return e; @@ -227,8 +229,9 @@ yout << files; } -Loader::Loader(FileSpec root) - : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {} +Loader::Loader(FileSpec root, bool passive) + : m_root(MakeAbsolute(std::move(root))), m_loaded(false), + m_passive_replay(passive) {} llvm::Error Loader::LoadIndex() { if (m_loaded) diff --git a/lldb/source/Utility/ReproducerInstrumentation.cpp b/lldb/source/Utility/ReproducerInstrumentation.cpp --- a/lldb/source/Utility/ReproducerInstrumentation.cpp +++ b/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; @@ -120,7 +120,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; } @@ -145,6 +145,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; @@ -181,4 +197,25 @@ UpdateBoundary(); } +void InstrumentationData::Initialize(Serializer &serializer, + Registry ®istry) { + InstanceImpl().emplace(serializer, registry); +} + +void InstrumentationData::Initialize(Deserializer &deserializer, + Registry ®istry) { + InstanceImpl().emplace(deserializer, registry); +} + +InstrumentationData &InstrumentationData::Instance() { + if (!InstanceImpl()) + InstanceImpl().emplace(); + return *InstanceImpl(); +} + +llvm::Optional &InstrumentationData::InstanceImpl() { + static llvm::Optional g_instrumentation_data; + return g_instrumentation_data; +} + bool lldb_private::repro::Recorder::g_global_boundary; diff --git a/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp b/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp --- a/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp +++ b/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp @@ -52,23 +52,62 @@ static llvm::Optional g_registry; static llvm::Optional g_serializer; +static llvm::Optional g_deserializer; -inline InstrumentationData GetTestInstrumentationData() { +class TestInstrumentationData : public InstrumentationData { +public: + TestInstrumentationData() : InstrumentationData() {} + TestInstrumentationData(Serializer &serializer, Registry ®istry) + : InstrumentationData(serializer, registry) {} + TestInstrumentationData(Deserializer &deserializer, Registry ®istry) + : InstrumentationData(deserializer, registry) {} +}; + +inline TestInstrumentationData GetTestInstrumentationData() { + assert(!(g_serializer && g_deserializer)); if (g_serializer) - return InstrumentationData(*g_serializer, *g_registry); - return InstrumentationData(); + return TestInstrumentationData(*g_serializer, *g_registry); + if (g_deserializer) + return TestInstrumentationData(*g_deserializer, *g_registry); + return TestInstrumentationData(); } class TestInstrumentationDataRAII { -public: +private: TestInstrumentationDataRAII(llvm::raw_string_ostream &os) { g_registry.emplace(); g_serializer.emplace(os); + g_deserializer.reset(); } - ~TestInstrumentationDataRAII() { + TestInstrumentationDataRAII(llvm::StringRef buffer) { + g_registry.emplace(); + g_serializer.reset(); + g_deserializer.emplace(buffer); + } + + friend std::unique_ptr + std::make_unique(llvm::raw_string_ostream &os); + friend std::unique_ptr + std::make_unique(llvm::StringRef &buffer); + +public: + ~TestInstrumentationDataRAII() { Reset(); } + + void Reset() { g_registry.reset(); g_serializer.reset(); + g_deserializer.reset(); + } + + static std::unique_ptr + GetRecordingData(llvm::raw_string_ostream &os) { + return std::make_unique(os); + } + + static std::unique_ptr + GetReplayData(llvm::StringRef buffer) { + return std::make_unique(buffer); } }; @@ -95,11 +134,17 @@ InstrumentedFoo(const InstrumentedFoo &foo); InstrumentedFoo &operator=(const InstrumentedFoo &foo); void A(int a); + int GetA(); void B(int &b) const; + int &GetB(); int C(float *c); + float GetC(); int D(const char *d) const; + void GetD(char *buffer, size_t length); static void E(double e); + double GetE(); static int F(); + bool GetF(); void Validate() override; //// } virtual bool IsA(Class c) override { return c == Class::Foo; } @@ -182,35 +227,71 @@ m_a = a; } +int InstrumentedFoo::GetA() { + LLDB_RECORD_METHOD_NO_ARGS(int, InstrumentedFoo, GetA); + + return m_a; +} + void InstrumentedFoo::B(int &b) const { LLDB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b); m_called++; m_b = b; } +int &InstrumentedFoo::GetB() { + LLDB_RECORD_METHOD_NO_ARGS(int &, InstrumentedFoo, GetB); + + return m_b; +} + int InstrumentedFoo::C(float *c) { LLDB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c); m_c = *c; return 1; } +float InstrumentedFoo::GetC() { + LLDB_RECORD_METHOD_NO_ARGS(float, InstrumentedFoo, GetC); + + return m_c; +} + int InstrumentedFoo::D(const char *d) const { LLDB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d); m_d = std::string(d); return 2; } +void InstrumentedFoo::GetD(char *buffer, size_t length) { + LLDB_RECORD_METHOD(void, InstrumentedFoo, GetD, (char *, size_t), buffer, + length); + ::snprintf(buffer, length, "%s", m_d.c_str()); +} + void InstrumentedFoo::E(double e) { LLDB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e); g_e = e; } +double InstrumentedFoo::GetE() { + LLDB_RECORD_METHOD_NO_ARGS(double, InstrumentedFoo, GetE); + + return g_e; +} + int InstrumentedFoo::F() { LLDB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F); g_f = true; return 3; } +bool InstrumentedFoo::GetF() { + LLDB_RECORD_METHOD_NO_ARGS(bool, InstrumentedFoo, GetF); + + return g_f; +} + void InstrumentedFoo::Validate() { LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedFoo, Validate); EXPECT_EQ(m_a, 100); @@ -296,6 +377,12 @@ LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo, (InstrumentedFoo &)); LLDB_REGISTER_METHOD(void, InstrumentedBar, Validate, ()); + LLDB_REGISTER_METHOD(int, InstrumentedFoo, GetA, ()); + LLDB_REGISTER_METHOD(int &, InstrumentedFoo, GetB, ()); + LLDB_REGISTER_METHOD(float, InstrumentedFoo, GetC, ()); + LLDB_REGISTER_METHOD(void, InstrumentedFoo, GetD, (char *, size_t)); + LLDB_REGISTER_METHOD(double, InstrumentedFoo, GetE, ()); + LLDB_REGISTER_METHOD(bool, InstrumentedFoo, GetF, ()); } static const Pod p; @@ -534,7 +621,7 @@ llvm::raw_string_ostream os(str); { - TestInstrumentationDataRAII data(os); + auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; @@ -563,7 +650,7 @@ llvm::raw_string_ostream os(str); { - TestInstrumentationDataRAII data(os); + auto data = TestInstrumentationDataRAII::GetRecordingData(os); int b = 200; float c = 300.3f; @@ -603,7 +690,7 @@ llvm::raw_string_ostream os(str); { - TestInstrumentationDataRAII data(os); + auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo foo = bar.GetInstrumentedFoo(); @@ -643,7 +730,7 @@ llvm::raw_string_ostream os(str); { - TestInstrumentationDataRAII data(os); + auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); @@ -679,7 +766,7 @@ llvm::raw_string_ostream os(str); { - TestInstrumentationDataRAII data(os); + auto data = TestInstrumentationDataRAII::GetRecordingData(os); InstrumentedBar bar; InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); @@ -709,3 +796,302 @@ deserializer.GetAllObjects(), {{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}}); } + +TEST(PassiveReplayTest, InstrumentedFoo) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + int b = 200; + float c = 300.3f; + double e = 400.4; + + InstrumentedFoo foo(0); + foo.A(100); + foo.B(b); + foo.C(&c); + foo.D("bar"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + } + + std::string buffer = os.str(); + + { + auto data = TestInstrumentationDataRAII::GetReplayData(buffer); + + int b = 999; + float c = 999.9f; + double e = 999.9; + + InstrumentedFoo foo(9); + foo.A(999); + foo.B(b); + foo.C(&c); + foo.D("999"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + } +} + +TEST(PassiveReplayTest, InstrumentedFooInvalid) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + int b = 200; + float c = 300.3f; + double e = 400.4; + + InstrumentedFoo foo(0); + foo.A(100); + foo.B(b); + foo.C(&c); + foo.D("bar"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + } + + std::string buffer = os.str(); + + { + auto data = TestInstrumentationDataRAII::GetReplayData(buffer); + + int b = 999; + float c = 999.9f; + double e = 999.9; + + InstrumentedFoo foo(9); + foo.A(999); + foo.B(b); + foo.C(&c); + foo.D("999"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + // Detect divergence. + EXPECT_DEATH(foo.GetA(), ""); + } +} + +TEST(PassiveReplayTest, InstrumentedBar) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + InstrumentedBar bar; + InstrumentedFoo foo = bar.GetInstrumentedFoo(); + + int b = 200; + float c = 300.3f; + double e = 400.4; + + foo.A(100); + foo.B(b); + foo.C(&c); + foo.D("bar"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } + + std::string buffer = os.str(); + + { + auto data = TestInstrumentationDataRAII::GetReplayData(buffer); + + InstrumentedBar bar; + InstrumentedFoo foo = bar.GetInstrumentedFoo(); + + int b = 99; + float c = 999.9f; + double e = 999.9; + + foo.A(999); + foo.B(b); + foo.C(&c); + foo.D("999"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } +} + +TEST(PassiveReplayTest, InstrumentedBarRef) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + InstrumentedBar bar; + InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); + + int b = 200; + float c = 300.3f; + double e = 400.4; + + foo.A(100); + foo.B(b); + foo.C(&c); + foo.D("bar"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } + + std::string buffer = os.str(); + + { + auto data = TestInstrumentationDataRAII::GetReplayData(buffer); + + InstrumentedBar bar; + InstrumentedFoo &foo = bar.GetInstrumentedFooRef(); + + int b = 99; + float c = 999.9f; + double e = 999.9; + + foo.A(999); + foo.B(b); + foo.C(&c); + foo.D("999"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } +} + +TEST(PassiveReplayTest, InstrumentedBarPtr) { + std::string str; + llvm::raw_string_ostream os(str); + + { + auto data = TestInstrumentationDataRAII::GetRecordingData(os); + + InstrumentedBar bar; + InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); + + int b = 200; + float c = 300.3f; + double e = 400.4; + + foo.A(100); + foo.B(b); + foo.C(&c); + foo.D("bar"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } + + std::string buffer = os.str(); + + { + auto data = TestInstrumentationDataRAII::GetReplayData(buffer); + + InstrumentedBar bar; + InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr()); + + int b = 99; + float c = 999.9f; + double e = 999.9; + + foo.A(999); + foo.B(b); + foo.C(&c); + foo.D("999"); + InstrumentedFoo::E(e); + InstrumentedFoo::F(); + foo.Validate(); + + EXPECT_EQ(foo.GetA(), 100); + EXPECT_EQ(foo.GetB(), 200); + EXPECT_NEAR(foo.GetC(), 300.3, 0.01); + EXPECT_NEAR(foo.GetE(), 400.4, 0.01); + EXPECT_EQ(foo.GetF(), true); + + bar.SetInstrumentedFoo(foo); + bar.SetInstrumentedFoo(&foo); + bar.Validate(); + } +}