JDevlieghere updated this revision to Diff 256505. JDevlieghere added a comment.
- Simplify `LLDB_RECORD_CONSTRUCTOR` macro. The other macros have `return` statements that need to be inlined in the caller for replay, and the boundary tracking needs to be updated right, so I'm not convinced the extra complexity is worth the deduplication. - Use the replayer ID to check the signature. I didn't get around to adding the unit tests yet. I noticed that this change breaks something and I'm still debugging it. I think it's specific to the unit test though, as the test suite isn't affected. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D77602/new/ https://reviews.llvm.org/D77602 Files: lldb/include/lldb/Utility/Reproducer.h lldb/include/lldb/Utility/ReproducerInstrumentation.h lldb/source/API/SBDebugger.cpp lldb/source/API/SBReproducer.cpp lldb/source/API/SBReproducerPrivate.h lldb/source/Utility/Reproducer.cpp lldb/source/Utility/ReproducerInstrumentation.cpp
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 <thread> #include <stdio.h> #include <stdlib.h> +#include <thread> 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; 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/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<SBProvider::Info>(); + 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/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/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ lldb/source/API/SBDebugger.cpp @@ -1629,31 +1629,31 @@ template <> void RegisterMethods<SBDebugger>(Registry &R) { // Custom implementation. - R.Register(&invoke<void (SBDebugger::*)( - FILE *, bool)>::method<&SBDebugger::SetErrorFileHandle>::doit, + R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method< + &SBDebugger::SetErrorFileHandle>::record, &SetFileHandleRedirect); - R.Register(&invoke<void (SBDebugger::*)( - FILE *, bool)>::method<&SBDebugger::SetOutputFileHandle>::doit, + R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method< + &SBDebugger::SetOutputFileHandle>::record, &SetFileHandleRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - SBFile)>::method<&SBDebugger::SetInputFile>::doit, + SBFile)>::method<&SBDebugger::SetInputFile>::record, &SetFileRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - SBFile)>::method<&SBDebugger::SetOutputFile>::doit, + SBFile)>::method<&SBDebugger::SetOutputFile>::record, &SetFileRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - SBFile)>::method<&SBDebugger::SetErrorFile>::doit, + SBFile)>::method<&SBDebugger::SetErrorFile>::record, &SetFileRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - FileSP)>::method<&SBDebugger::SetInputFile>::doit, + FileSP)>::method<&SBDebugger::SetInputFile>::record, &SetFileRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - FileSP)>::method<&SBDebugger::SetOutputFile>::doit, + FileSP)>::method<&SBDebugger::SetOutputFile>::record, &SetFileRedirect); R.Register(&invoke<SBError (SBDebugger::*)( - FileSP)>::method<&SBDebugger::SetErrorFile>::doit, + FileSP)>::method<&SBDebugger::SetErrorFile>::record, &SetFileRedirect); LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(bool, SBDebugger, 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<Class * Signature>(&construct<Class Signature>::doit, "", #Class, \ - #Class, #Signature) + R.Register<Class * Signature>(&construct<Class Signature>::record, "", \ + #Class, #Class, #Signature) #define LLDB_REGISTER_METHOD(Result, Class, Method, Signature) \ R.Register( \ - &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::doit, \ + &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature) \ R.Register(&invoke<Result(Class::*) Signature const>::method_const<( \ - &Class::Method)>::doit, \ + &Class::Method)>::record, \ #Result, #Class, #Method, #Signature) #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ - R.Register<Result Signature>( \ - static_cast<Result(*) Signature>(&Class::Method), #Result, #Class, \ - #Method, #Signature) + R.Register( \ + &invoke<Result(*) Signature>::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<Result>(Class::Method, s, l); \ - }; \ - R.Register<Result(char *, size_t)>( \ - static_cast<Result (*)(char *, size_t)>(&Class::Method), _redirect, \ - #Result, #Class, #Method, "(char*, size_t"); \ + R.Register(&invoke<Result (*)(char *, size_t)>::method_static<( \ + &Class::Method)>::record, \ + &char_ptr_redirect<Result (*)(char *, size_t)>::method_static<( \ + &Class::Method)>::record, \ + #Result, #Class, #Method, "(char*, size_t"); \ } #define LLDB_REGISTER_CHAR_PTR_REDIRECT(Result, Class, Method) \ { \ R.Register(&invoke<Result (Class::*)(char *, size_t)>::method<( \ - &Class::Method)>::doit, \ + &Class::Method)>::record, \ &char_ptr_redirect<Result (Class::*)(char *, size_t)>::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<Result (Class::*)(char *, size_t) \ - const>::method_const<(&Class::Method)>::doit, \ + const>::method_const<(&Class::Method)>::record, \ &char_ptr_redirect<Result (Class::*)(char *, size_t) \ - const>::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<Class Signature>::doit, \ - __VA_ARGS__); \ - _recorder.RecordResult(this, false); \ + { \ + lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + lldb_private::repro::construct<Class Signature>::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<Class()>::doit); \ - _recorder.RecordResult(this, false); \ + { \ + lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ + lldb_private::repro::construct<Class()>::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<Result( \ - Class::*) Signature>::method<(&Class::Method)>::doit, \ - this, __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke<Result(Class::*) Signature>::method<( \ + &Class::Method)>::record, \ + this, __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke<Result(Class::*) Signature>:: \ + 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<Result( \ - Class::*) Signature const>::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<Result(Class::*) Signature const>:: \ + 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<Result(Class::*) Signature const>:: \ + 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<Result ( \ - Class::*)()>::method<(&Class::Method)>::doit, \ - this); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record(*_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke<Result ( \ + Class::*)()>::method<(&Class::Method)>::record, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke<Result (Class::*)()>::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<Result ( \ - Class::*)() const>::method_const<(&Class::Method)>::doit, \ - this); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + &lldb_private::repro::invoke<Result ( \ + Class::*)() const>::method_const<(&Class::Method)>::record, \ + this); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke<Result (Class::*)() const>:: \ + 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<Result(*) Signature>(&Class::Method), \ - __VA_ARGS__); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + lldb_private::repro::invoke<Result(*) Signature>::method_static<( \ + &Class::Method)>::record, \ + __VA_ARGS__); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke<Result(*) Signature>:: \ + 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<Result (*)()>(&Class::Method)); \ + if (lldb_private::repro::Serializer *_serializer = \ + _data.GetSerializer()) { \ + _recorder.Record( \ + *_serializer, _data.GetRegistry(), \ + lldb_private::repro::invoke<Result (*)()>::method_static<( \ + &Class::Method)>::record); \ + } else if (lldb_private::repro::Deserializer *_deserializer = \ + _data.GetDeserializer()) { \ + if (_recorder.ShouldCapture()) { \ + return lldb_private::repro::invoke<Result (*)()>::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 <typename T> void AddObjectForIndex(unsigned idx, T *object) { + template <typename T> T *AddObjectForIndex(unsigned idx, T *object) { AddObjectForIndexImpl( idx, static_cast<void *>( const_cast<typename std::remove_const<T>::type *>(object))); + return object; } /// Adds a reference to an object to the mapping for the given index. - template <typename T> void AddObjectForIndex(unsigned idx, T &object) { + template <typename T> T &AddObjectForIndex(unsigned idx, T &object) { AddObjectForIndexImpl( idx, static_cast<void *>( const_cast<typename std::remove_const<T>::type *>(&object))); + return object; } private: @@ -321,20 +390,20 @@ } /// Store the returned value in the index-to-object mapping. - template <typename T> void HandleReplayResult(const T &t) { + template <typename T> T &HandleReplayResult(const T &t) { unsigned result = Deserialize<unsigned>(); if (is_trivially_serializable<T>::value) - return; + return const_cast<T &>(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 <typename T> void HandleReplayResult(T *t) { + template <typename T> T *HandleReplayResult(T *t) { unsigned result = Deserialize<unsigned>(); if (is_trivially_serializable<T>::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<Args...>::template deserialized<Result>::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<Args...>::template deserialized<void>::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> replayer, SignatureStr signature); private: - std::string GetSignature(unsigned id); - Replayer *GetReplayer(unsigned id); - /// Mapping of function addresses to replayers and their ID. std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>> m_replayers; @@ -532,37 +613,6 @@ std::map<unsigned, std::pair<Replayer *, SignatureStr>> m_ids; }; -/// To be used as the "Runtime ID" of a constructor. It also invokes the -/// constructor when called. -template <typename Signature> struct construct; -template <typename Class, typename... Args> struct construct<Class(Args...)> { - 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 <typename Signature> struct invoke; -template <typename Result, typename Class, typename... Args> -struct invoke<Result (Class::*)(Args...)> { - template <Result (Class::*m)(Args...)> struct method { - static Result doit(Class *c, Args... args) { return (c->*m)(args...); } - }; -}; - -template <typename Result, typename Class, typename... Args> -struct invoke<Result (Class::*)(Args...) const> { - template <Result (Class::*m)(Args...) const> struct method_const { - static Result doit(Class *c, Args... args) { return (c->*m)(args...); } - }; -}; - -template <typename Class, typename... Args> -struct invoke<void (Class::*)(Args...)> { - template <void (Class::*m)(Args...)> 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<Result>(r); } + template <typename Result> + Result ReplayResult(Result &&r, bool update_boundary) { + if (update_boundary) + UpdateBoundary(); + return std::forward<Result>(r); + } + + bool ShouldCapture() { return m_local_boundary; } + private: + template <typename T> 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 <typename Signature> struct construct; +template <typename Class, typename... Args> struct construct<Class(Args...)> { + 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<Class(Args...)>::record, + args...); + recorder.RecordResult(c, false); + } else if (Deserializer *deserializer = data.GetDeserializer()) { + if (recorder.ShouldCapture()) { + lldb_private::repro::construct<Class(Args...)>::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<unsigned>(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult( + static_cast<DefaultReplayer<Class *(Args...)> *>( + 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 <typename Signature> struct invoke; +template <typename Result, typename Class, typename... Args> +struct invoke<Result (Class::*)(Args...)> { + template <Result (Class::*m)(Args...)> 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<unsigned>(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult<Result>( + static_cast<DefaultReplayer<Result(Class *, Args...)> *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template <typename Class, typename... Args> +struct invoke<void (Class::*)(Args...)> { + template <void (Class::*m)(Args...)> 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<unsigned>(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template <typename Result, typename Class, typename... Args> +struct invoke<Result (Class::*)(Args...) const> { + template <Result (Class::*m)(Args...) const> 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<unsigned>(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult<Result>( + static_cast<DefaultReplayer<Result(Class *, Args...)> *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template <typename Class, typename... Args> +struct invoke<void (Class::*)(Args...) const> { + template <void (Class::*m)(Args...) const> 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<unsigned>(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + +template <typename Signature> struct replay; + +template <typename Result, typename Class, typename... Args> +struct replay<Result (Class::*)(Args...)> { + template <Result (Class::*m)(Args...)> struct method {}; +}; + +template <typename Result, typename... Args> +struct invoke<Result (*)(Args...)> { + template <Result (*m)(Args...)> 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<unsigned>(); + registry.CheckID(id, actual_id); + return recorder.ReplayResult<Result>( + static_cast<DefaultReplayer<Result(Args...)> *>( + registry.GetReplayer(id)) + ->Replay(deserializer), + true); + } + }; +}; + +template <typename... Args> struct invoke<void (*)(Args...)> { + template <void (*m)(Args...)> 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<unsigned>(); + registry.CheckID(id, actual_id); + registry.GetReplayer(id)->operator()(deserializer); + } + }; +}; + template <typename Signature> struct char_ptr_redirect; template <typename Result, typename Class> struct char_ptr_redirect<Result (Class::*)(char *, size_t) const> { template <Result (Class::*m)(char *, size_t) const> 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<char *>(calloc(l, sizeof(char))); return (c->*m)(buffer, l); } @@ -817,19 +1031,21 @@ template <typename Result, typename Class> struct char_ptr_redirect<Result (Class::*)(char *, size_t)> { template <Result (Class::*m)(char *, size_t)> 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<char *>(calloc(l, sizeof(char))); return (c->*m)(buffer, l); } }; }; - template <typename Result> -Result char_ptr_redirect_static(Result (*f)(char *, size_t), char *s, - size_t l) { - char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char))); - return f(buffer, l); -} +struct char_ptr_redirect<Result (*)(char *, size_t)> { + template <Result (*m)(char *, size_t)> struct method_static { + static Result record(char *s, size_t l) { + char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char))); + return (*m)(buffer, l); + } + }; +}; } // namespace repro } // namespace lldb_private 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<std::string> m_files; bool m_loaded; + bool m_passive_replay; }; /// The reproducer enables clients to obtain access to the Generator and
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits