| //===-- ReproducerInstrumentation.h -----------------------------*- C++ -*-===// |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H |
| #define LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H |
| |
| #include "lldb/Utility/FileSpec.h" |
| #include "lldb/Utility/Log.h" |
| #include "lldb/Utility/Logging.h" |
| |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #include <map> |
| #include <thread> |
| #include <type_traits> |
| |
| template <typename T, |
| typename std::enable_if<std::is_fundamental<T>::value, int>::type = 0> |
| inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) { |
| ss << t; |
| } |
| |
| template <typename T, typename std::enable_if<!std::is_fundamental<T>::value, |
| int>::type = 0> |
| inline void stringify_append(llvm::raw_string_ostream &ss, const T &t) { |
| ss << &t; |
| } |
| |
| template <typename T> |
| inline void stringify_append(llvm::raw_string_ostream &ss, T *t) { |
| ss << reinterpret_cast<void *>(t); |
| } |
| |
| template <typename T> |
| inline void stringify_append(llvm::raw_string_ostream &ss, const T *t) { |
| ss << reinterpret_cast<const void *>(t); |
| } |
| |
| template <> |
| inline void stringify_append<char>(llvm::raw_string_ostream &ss, |
| const char *t) { |
| ss << '\"' << t << '\"'; |
| } |
| |
| template <> |
| inline void stringify_append<std::nullptr_t>(llvm::raw_string_ostream &ss, |
| const std::nullptr_t &t) { |
| ss << "\"nullptr\""; |
| } |
| |
| template <typename Head> |
| inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head) { |
| stringify_append(ss, head); |
| } |
| |
| template <typename Head, typename... Tail> |
| inline void stringify_helper(llvm::raw_string_ostream &ss, const Head &head, |
| const Tail &... tail) { |
| stringify_append(ss, head); |
| ss << ", "; |
| stringify_helper(ss, tail...); |
| } |
| |
| template <typename... Ts> inline std::string stringify_args(const Ts &... ts) { |
| std::string buffer; |
| llvm::raw_string_ostream ss(buffer); |
| stringify_helper(ss, ts...); |
| return ss.str(); |
| } |
| |
| // Define LLDB_REPRO_INSTR_TRACE to trace to stderr instead of LLDB's log |
| // infrastructure. This is useful when you need to see traces before the logger |
| // is initialized or enabled. |
| // #define LLDB_REPRO_INSTR_TRACE |
| |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| inline llvm::raw_ostream &this_thread_id() { |
| size_t tid = std::hash<std::thread::id>{}(std::this_thread::get_id()); |
| return llvm::errs().write_hex(tid) << " :: "; |
| } |
| #endif |
| |
| #define LLDB_REGISTER_CONSTRUCTOR(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)>::record, \ |
| #Result, #Class, #Method, #Signature) |
| |
| #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature) \ |
| R.Register(&invoke<Result(Class::*) \ |
| Signature const>::method<(&Class::Method)>::record, \ |
| #Result, #Class, #Method, #Signature) |
| |
| #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature) \ |
| R.Register(&invoke<Result(*) Signature>::method<(&Class::Method)>::record, \ |
| #Result, #Class, #Method, #Signature) |
| |
| #define LLDB_REGISTER_CHAR_PTR_METHOD_STATIC(Result, Class, Method) \ |
| R.Register( \ |
| &invoke<Result (*)(char *, size_t)>::method<(&Class::Method)>::record, \ |
| &invoke_char_ptr<Result (*)(char *, \ |
| size_t)>::method<(&Class::Method)>::record, \ |
| #Result, #Class, #Method, "(char*, size_t"); |
| |
| #define LLDB_REGISTER_CHAR_PTR_METHOD(Result, Class, Method) \ |
| R.Register(&invoke<Result (Class::*)(char *, size_t)>::method<( \ |
| &Class::Method)>::record, \ |
| &invoke_char_ptr<Result (Class::*)(char *, size_t)>::method<( \ |
| &Class::Method)>::record, \ |
| #Result, #Class, #Method, "(char*, size_t"); |
| |
| #define LLDB_REGISTER_CHAR_PTR_METHOD_CONST(Result, Class, Method) \ |
| R.Register(&invoke<Result (Class::*)(char *, size_t) \ |
| const>::method<(&Class::Method)>::record, \ |
| &invoke_char_ptr<Result (Class::*)(char *, size_t) \ |
| const>::method<(&Class::Method)>::record, \ |
| #Result, #Class, #Method, "(char*, size_t"); |
| |
| #define LLDB_CONSTRUCT_(T, Class, ...) \ |
| lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION); \ |
| lldb_private::repro::construct<T>::handle(LLDB_GET_INSTRUMENTATION_DATA(), \ |
| _recorder, Class, __VA_ARGS__); |
| |
| #define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ |
| LLDB_CONSTRUCT_(Class Signature, this, __VA_ARGS__) |
| |
| #define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \ |
| 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()) { \ |
| if (lldb_private::repro::Serializer *_serializer = \ |
| _data.GetSerializer()) { \ |
| _recorder.Record(*_serializer, _data.GetRegistry(), \ |
| &lldb_private::repro::invoke<T1>::method<T2>::record, \ |
| __VA_ARGS__); \ |
| } else if (lldb_private::repro::Deserializer *_deserializer = \ |
| _data.GetDeserializer()) { \ |
| if (_recorder.ShouldCapture()) { \ |
| return lldb_private::repro::invoke<T1>::method<T2>::replay( \ |
| _recorder, *_deserializer, _data.GetRegistry()); \ |
| } \ |
| } \ |
| } |
| |
| #define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ |
| LLDB_RECORD_(Result(Class::*) Signature, (&Class::Method), this, __VA_ARGS__) |
| |
| #define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \ |
| LLDB_RECORD_(Result(Class::*) Signature const, (&Class::Method), this, \ |
| __VA_ARGS__) |
| |
| #define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \ |
| LLDB_RECORD_(Result (Class::*)(), (&Class::Method), this) |
| |
| #define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \ |
| LLDB_RECORD_(Result (Class::*)() const, (&Class::Method), this) |
| |
| #define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \ |
| LLDB_RECORD_(Result(*) Signature, (&Class::Method), __VA_ARGS__) |
| |
| #define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method) \ |
| LLDB_RECORD_(Result (*)(), (&Class::Method), lldb_private::repro::EmptyArg()) |
| |
| #define LLDB_RECORD_CHAR_PTR_(T1, T2, StrOut, ...) \ |
| lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION, \ |
| stringify_args(__VA_ARGS__)); \ |
| if (lldb_private::repro::InstrumentationData _data = \ |
| LLDB_GET_INSTRUMENTATION_DATA()) { \ |
| if (lldb_private::repro::Serializer *_serializer = \ |
| _data.GetSerializer()) { \ |
| _recorder.Record(*_serializer, _data.GetRegistry(), \ |
| &lldb_private::repro::invoke<T1>::method<(T2)>::record, \ |
| __VA_ARGS__); \ |
| } else if (lldb_private::repro::Deserializer *_deserializer = \ |
| _data.GetDeserializer()) { \ |
| if (_recorder.ShouldCapture()) { \ |
| return lldb_private::repro::invoke_char_ptr<T1>::method<T2>::replay( \ |
| _recorder, *_deserializer, _data.GetRegistry(), StrOut); \ |
| } \ |
| } \ |
| } |
| |
| #define LLDB_RECORD_CHAR_PTR_METHOD(Result, Class, Method, Signature, StrOut, \ |
| ...) \ |
| LLDB_RECORD_CHAR_PTR_(Result(Class::*) Signature, (&Class::Method), StrOut, \ |
| this, __VA_ARGS__) |
| |
| #define LLDB_RECORD_CHAR_PTR_METHOD_CONST(Result, Class, Method, Signature, \ |
| StrOut, ...) \ |
| LLDB_RECORD_CHAR_PTR_(Result(Class::*) Signature const, (&Class::Method), \ |
| StrOut, this, __VA_ARGS__) |
| |
| #define LLDB_RECORD_CHAR_PTR_STATIC_METHOD(Result, Class, Method, Signature, \ |
| StrOut, ...) \ |
| LLDB_RECORD_CHAR_PTR_(Result(*) Signature, (&Class::Method), StrOut, \ |
| __VA_ARGS__) |
| |
| #define LLDB_RECORD_RESULT(Result) _recorder.RecordResult(Result, true); |
| |
| /// The LLDB_RECORD_DUMMY macro is special because it doesn't actually record |
| /// anything. It's used to track API boundaries when we cannot record for |
| /// technical reasons. |
| #define LLDB_RECORD_DUMMY(Result, Class, Method, Signature, ...) \ |
| lldb_private::repro::Recorder _recorder; |
| |
| #define LLDB_RECORD_DUMMY_NO_ARGS(Result, Class, Method) \ |
| lldb_private::repro::Recorder _recorder; |
| |
| namespace lldb_private { |
| namespace repro { |
| |
| template <class T> |
| struct is_trivially_serializable |
| : std::integral_constant<bool, std::is_fundamental<T>::value || |
| std::is_enum<T>::value> {}; |
| |
| /// Mapping between serialized indices and their corresponding objects. |
| /// |
| /// This class is used during replay to map indices back to in-memory objects. |
| /// |
| /// When objects are constructed, they are added to this mapping using |
| /// AddObjectForIndex. |
| /// |
| /// When an object is passed to a function, its index is deserialized and |
| /// AddObjectForIndex returns the corresponding object. If there is no object |
| /// for the given index, a nullptr is returend. The latter is valid when custom |
| /// replay code is in place and the actual object is ignored. |
| class IndexToObject { |
| public: |
| /// Returns an object as a pointer for the given index or nullptr if not |
| /// present in the map. |
| template <typename T> T *GetObjectForIndex(unsigned idx) { |
| assert(idx != 0 && "Cannot get object for sentinel"); |
| void *object = GetObjectForIndexImpl(idx); |
| return static_cast<T *>(object); |
| } |
| |
| /// Adds a pointer to an object to the mapping for the given index. |
| 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> T &AddObjectForIndex(unsigned idx, T &object) { |
| AddObjectForIndexImpl( |
| idx, static_cast<void *>( |
| const_cast<typename std::remove_const<T>::type *>(&object))); |
| return object; |
| } |
| |
| /// Get all objects sorted by their index. |
| std::vector<void *> GetAllObjects() const; |
| |
| private: |
| /// Helper method that does the actual lookup. The void* result is later cast |
| /// by the caller. |
| void *GetObjectForIndexImpl(unsigned idx); |
| |
| /// Helper method that does the actual insertion. |
| void AddObjectForIndexImpl(unsigned idx, void *object); |
| |
| /// Keeps a mapping between indices and their corresponding object. |
| llvm::DenseMap<unsigned, void *> m_mapping; |
| }; |
| |
| /// We need to differentiate between pointers to fundamental and |
| /// non-fundamental types. See the corresponding Deserializer::Read method |
| /// for the reason why. |
| struct PointerTag {}; |
| struct ReferenceTag {}; |
| struct ValueTag {}; |
| struct FundamentalPointerTag {}; |
| struct FundamentalReferenceTag {}; |
| |
| /// Return the deserialization tag for the given type T. |
| template <class T> struct serializer_tag { |
| typedef typename std::conditional<std::is_trivially_copyable<T>::value, |
| ValueTag, ReferenceTag>::type type; |
| }; |
| template <class T> struct serializer_tag<T *> { |
| typedef |
| typename std::conditional<std::is_fundamental<T>::value, |
| FundamentalPointerTag, PointerTag>::type type; |
| }; |
| template <class T> struct serializer_tag<T &> { |
| typedef typename std::conditional<std::is_fundamental<T>::value, |
| FundamentalReferenceTag, ReferenceTag>::type |
| type; |
| }; |
| |
| /// Deserializes data from a buffer. It is used to deserialize function indices |
| /// to replay, their arguments and return values. |
| /// |
| /// Fundamental types and strings are read by value. Objects are read by their |
| /// index, which get translated by the IndexToObject mapping maintained in |
| /// this class. |
| /// |
| /// Additional bookkeeping with regards to the IndexToObject is required to |
| /// deserialize objects. When a constructor is run or an object is returned by |
| /// value, we need to capture the object and add it to the index together with |
| /// its index. This is the job of HandleReplayResult(Void). |
| class Deserializer { |
| public: |
| Deserializer(llvm::StringRef buffer) : m_buffer(buffer) {} |
| |
| /// Returns true when the buffer has unread data. |
| bool HasData(unsigned size) { return size <= m_buffer.size(); } |
| |
| /// Deserialize and interpret value as T. |
| template <typename T> T Deserialize() { |
| T t = Read<T>(typename serializer_tag<T>::type()); |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << " -> " |
| << stringify_args(t) << "\n"; |
| #endif |
| return t; |
| } |
| |
| template <typename T> const T &HandleReplayResult(const T &t) { |
| CheckSequence(Deserialize<unsigned>()); |
| unsigned result = Deserialize<unsigned>(); |
| if (is_trivially_serializable<T>::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 <typename T> T &HandleReplayResult(T &t) { |
| CheckSequence(Deserialize<unsigned>()); |
| unsigned result = Deserialize<unsigned>(); |
| if (is_trivially_serializable<T>::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 <typename T> T *HandleReplayResult(T *t) { |
| CheckSequence(Deserialize<unsigned>()); |
| unsigned result = Deserialize<unsigned>(); |
| if (is_trivially_serializable<T>::value) |
| return t; |
| return m_index_to_object.AddObjectForIndex(result, t); |
| } |
| |
| /// All returned types are recorded, even when the function returns a void. |
| /// The latter requires special handling. |
| void HandleReplayResultVoid() { |
| CheckSequence(Deserialize<unsigned>()); |
| unsigned result = Deserialize<unsigned>(); |
| assert(result == 0); |
| (void)result; |
| } |
| |
| std::vector<void *> GetAllObjects() const { |
| return m_index_to_object.GetAllObjects(); |
| } |
| |
| void SetExpectedSequence(unsigned sequence) { |
| m_expected_sequence = sequence; |
| } |
| |
| private: |
| template <typename T> T Read(ValueTag) { |
| assert(HasData(sizeof(T))); |
| T t; |
| std::memcpy(reinterpret_cast<char *>(&t), m_buffer.data(), sizeof(T)); |
| m_buffer = m_buffer.drop_front(sizeof(T)); |
| return t; |
| } |
| |
| template <typename T> T Read(PointerTag) { |
| typedef typename std::remove_pointer<T>::type UnderlyingT; |
| return m_index_to_object.template GetObjectForIndex<UnderlyingT>( |
| Deserialize<unsigned>()); |
| } |
| |
| template <typename T> T Read(ReferenceTag) { |
| typedef typename std::remove_reference<T>::type UnderlyingT; |
| // If this is a reference to a fundamental type we just read its value. |
| return *m_index_to_object.template GetObjectForIndex<UnderlyingT>( |
| Deserialize<unsigned>()); |
| } |
| |
| /// This method is used to parse references to fundamental types. Because |
| /// they're not recorded in the object table we have serialized their value. |
| /// We read its value, allocate a copy on the heap, and return a pointer to |
| /// the copy. |
| template <typename T> T Read(FundamentalPointerTag) { |
| typedef typename std::remove_pointer<T>::type UnderlyingT; |
| return new UnderlyingT(Deserialize<UnderlyingT>()); |
| } |
| |
| /// This method is used to parse references to fundamental types. Because |
| /// they're not recorded in the object table we have serialized their value. |
| /// We read its value, allocate a copy on the heap, and return a reference to |
| /// the copy. |
| template <typename T> T Read(FundamentalReferenceTag) { |
| // If this is a reference to a fundamental type we just read its value. |
| typedef typename std::remove_reference<T>::type UnderlyingT; |
| return *(new UnderlyingT(Deserialize<UnderlyingT>())); |
| } |
| |
| /// Verify that the given sequence number matches what we expect. |
| void CheckSequence(unsigned sequence); |
| |
| /// Mapping of indices to objects. |
| IndexToObject m_index_to_object; |
| |
| /// Buffer containing the serialized data. |
| llvm::StringRef m_buffer; |
| |
| /// The result's expected sequence number. |
| llvm::Optional<unsigned> m_expected_sequence; |
| }; |
| |
| /// Partial specialization for C-style strings. We read the string value |
| /// instead of treating it as pointer. |
| template <> const char *Deserializer::Deserialize<const char *>(); |
| template <> const char **Deserializer::Deserialize<const char **>(); |
| template <> const uint8_t *Deserializer::Deserialize<const uint8_t *>(); |
| template <> const void *Deserializer::Deserialize<const void *>(); |
| template <> char *Deserializer::Deserialize<char *>(); |
| template <> void *Deserializer::Deserialize<void *>(); |
| |
| /// Helpers to auto-synthesize function replay code. It deserializes the replay |
| /// function's arguments one by one and finally calls the corresponding |
| /// function. |
| template <typename... Remaining> struct DeserializationHelper; |
| |
| template <typename Head, typename... Tail> |
| struct DeserializationHelper<Head, Tail...> { |
| template <typename Result, typename... Deserialized> struct deserialized { |
| static Result doit(Deserializer &deserializer, |
| Result (*f)(Deserialized..., Head, Tail...), |
| Deserialized... d) { |
| return DeserializationHelper<Tail...>:: |
| template deserialized<Result, Deserialized..., Head>::doit( |
| deserializer, f, d..., deserializer.Deserialize<Head>()); |
| } |
| }; |
| }; |
| |
| template <> struct DeserializationHelper<> { |
| template <typename Result, typename... Deserialized> struct deserialized { |
| static Result doit(Deserializer &deserializer, Result (*f)(Deserialized...), |
| Deserialized... d) { |
| return f(d...); |
| } |
| }; |
| }; |
| |
| /// The replayer interface. |
| struct Replayer { |
| virtual ~Replayer() {} |
| virtual void operator()(Deserializer &deserializer) const = 0; |
| }; |
| |
| /// The default replayer deserializes the arguments and calls the function. |
| template <typename Signature> struct DefaultReplayer; |
| template <typename Result, typename... Args> |
| struct DefaultReplayer<Result(Args...)> : public Replayer { |
| DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {} |
| |
| void operator()(Deserializer &deserializer) const override { |
| Replay(deserializer); |
| } |
| |
| Result Replay(Deserializer &deserializer) const { |
| return deserializer.HandleReplayResult( |
| DeserializationHelper<Args...>::template deserialized<Result>::doit( |
| deserializer, f)); |
| } |
| |
| Result (*f)(Args...); |
| }; |
| |
| /// Partial specialization for function returning a void type. It ignores the |
| /// (absent) return value. |
| template <typename... Args> |
| struct DefaultReplayer<void(Args...)> : public Replayer { |
| 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(); |
| } |
| |
| void (*f)(Args...); |
| }; |
| |
| /// The registry contains a unique mapping between functions and their ID. The |
| /// IDs can be serialized and deserialized to replay a function. Functions need |
| /// to be registered with the registry for this to work. |
| class Registry { |
| private: |
| struct SignatureStr { |
| SignatureStr(llvm::StringRef result = {}, llvm::StringRef scope = {}, |
| llvm::StringRef name = {}, llvm::StringRef args = {}) |
| : result(result), scope(scope), name(name), args(args) {} |
| |
| std::string ToString() const; |
| |
| llvm::StringRef result; |
| llvm::StringRef scope; |
| llvm::StringRef name; |
| llvm::StringRef args; |
| }; |
| |
| public: |
| Registry() = default; |
| virtual ~Registry() = default; |
| |
| /// Register a default replayer for a function. |
| template <typename Signature> |
| void Register(Signature *f, llvm::StringRef result = {}, |
| llvm::StringRef scope = {}, llvm::StringRef name = {}, |
| llvm::StringRef args = {}) { |
| DoRegister(uintptr_t(f), std::make_unique<DefaultReplayer<Signature>>(f), |
| SignatureStr(result, scope, name, args)); |
| } |
| |
| /// Register a replayer that invokes a custom function with the same |
| /// signature as the replayed function. |
| template <typename Signature> |
| void Register(Signature *f, Signature *g, llvm::StringRef result = {}, |
| llvm::StringRef scope = {}, llvm::StringRef name = {}, |
| llvm::StringRef args = {}) { |
| DoRegister(uintptr_t(f), std::make_unique<DefaultReplayer<Signature>>(g), |
| SignatureStr(result, scope, name, args)); |
| } |
| |
| /// Replay functions from a file. |
| bool Replay(const FileSpec &file); |
| |
| /// Replay functions from a buffer. |
| bool Replay(llvm::StringRef buffer); |
| |
| /// Replay functions from a deserializer. |
| bool Replay(Deserializer &deserializer); |
| |
| /// 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: |
| /// Mapping of function addresses to replayers and their ID. |
| std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>> |
| m_replayers; |
| |
| /// Mapping of IDs to replayer instances. |
| std::map<unsigned, std::pair<Replayer *, SignatureStr>> m_ids; |
| }; |
| |
| /// Maps an object to an index for serialization. Indices are unique and |
| /// incremented for every new object. |
| /// |
| /// Indices start at 1 in order to differentiate with an invalid index (0) in |
| /// the serialized buffer. |
| class ObjectToIndex { |
| public: |
| template <typename T> unsigned GetIndexForObject(T *t) { |
| return GetIndexForObjectImpl(static_cast<const void *>(t)); |
| } |
| |
| private: |
| unsigned GetIndexForObjectImpl(const void *object); |
| |
| llvm::DenseMap<const void *, unsigned> m_mapping; |
| }; |
| |
| /// Serializes functions, their arguments and their return type to a stream. |
| class Serializer { |
| public: |
| Serializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(stream) {} |
| |
| /// Recursively serialize all the given arguments. |
| template <typename Head, typename... Tail> |
| void SerializeAll(const Head &head, const Tail &... tail) { |
| Serialize(head); |
| SerializeAll(tail...); |
| } |
| |
| void SerializeAll() { m_stream.flush(); } |
| |
| private: |
| /// Serialize pointers. We need to differentiate between pointers to |
| /// fundamental types (in which case we serialize its value) and pointer to |
| /// objects (in which case we serialize their index). |
| template <typename T> void Serialize(T *t) { |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| this_thread_id() << "Serializing with " << LLVM_PRETTY_FUNCTION << " -> " |
| << stringify_args(t) << "\n"; |
| #endif |
| if (std::is_fundamental<T>::value) { |
| Serialize(*t); |
| } else { |
| unsigned idx = m_tracker.GetIndexForObject(t); |
| Serialize(idx); |
| } |
| } |
| |
| /// Serialize references. We need to differentiate between references to |
| /// fundamental types (in which case we serialize its value) and references |
| /// to objects (in which case we serialize their index). |
| template <typename T> void Serialize(T &t) { |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| this_thread_id() << "Serializing with " << LLVM_PRETTY_FUNCTION << " -> " |
| << stringify_args(t) << "\n"; |
| #endif |
| if (is_trivially_serializable<T>::value) { |
| m_stream.write(reinterpret_cast<const char *>(&t), sizeof(T)); |
| } else { |
| unsigned idx = m_tracker.GetIndexForObject(&t); |
| Serialize(idx); |
| } |
| } |
| |
| void Serialize(const void *v) { |
| // FIXME: Support void* |
| } |
| |
| void Serialize(void *v) { |
| // FIXME: Support void* |
| } |
| |
| void Serialize(const char *t) { |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| this_thread_id() << "Serializing with " << LLVM_PRETTY_FUNCTION << " -> " |
| << stringify_args(t) << "\n"; |
| #endif |
| const size_t size = t ? strlen(t) : std::numeric_limits<size_t>::max(); |
| Serialize(size); |
| if (t) { |
| m_stream << t; |
| m_stream.write(0x0); |
| } |
| } |
| |
| void Serialize(const char **t) { |
| size_t size = 0; |
| if (!t) { |
| Serialize(size); |
| return; |
| } |
| |
| // Compute the size of the array. |
| const char *const *temp = t; |
| while (*temp++) |
| size++; |
| Serialize(size); |
| |
| // Serialize the content of the array. |
| while (*t) |
| Serialize(*t++); |
| } |
| |
| /// Serialization stream. |
| llvm::raw_ostream &m_stream; |
| |
| /// Mapping of objects to indices. |
| ObjectToIndex m_tracker; |
| }; // namespace repro |
| |
| class InstrumentationData { |
| public: |
| Serializer *GetSerializer() { return m_serializer; } |
| Deserializer *GetDeserializer() { return m_deserializer; } |
| Registry &GetRegistry() { return *m_registry; } |
| |
| 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<InstrumentationData, true>; |
| friend llvm::Optional<InstrumentationData>; |
| |
| 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<InstrumentationData> &InstanceImpl(); |
| |
| Serializer *m_serializer; |
| Deserializer *m_deserializer; |
| Registry *m_registry; |
| }; |
| |
| struct EmptyArg {}; |
| |
| /// RAII object that records function invocations and their return value. |
| /// |
| /// API calls are only captured when the API boundary is crossed. Once we're in |
| /// the API layer, and another API function is called, it doesn't need to be |
| /// recorded. |
| /// |
| /// When a call is recored, its result is always recorded as well, even if the |
| /// function returns a void. For functions that return by value, RecordResult |
| /// should be used. Otherwise a sentinel value (0) will be serialized. |
| /// |
| /// Because of the functional overlap between logging and recording API calls, |
| /// this class is also used for logging. |
| class Recorder { |
| public: |
| Recorder(); |
| Recorder(llvm::StringRef pretty_func, std::string &&pretty_args = {}); |
| ~Recorder(); |
| |
| /// Records a single function call. |
| template <typename Result, typename... FArgs, typename... RArgs> |
| void Record(Serializer &serializer, Registry ®istry, Result (*f)(FArgs...), |
| const RArgs &... args) { |
| m_serializer = &serializer; |
| if (!ShouldCapture()) |
| return; |
| |
| std::lock_guard<std::mutex> lock(g_mutex); |
| unsigned sequence = GetSequenceNumber(); |
| unsigned id = registry.GetID(uintptr_t(f)); |
| |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| Log(id); |
| #endif |
| |
| serializer.SerializeAll(sequence); |
| serializer.SerializeAll(id); |
| serializer.SerializeAll(args...); |
| |
| if (std::is_class<typename std::remove_pointer< |
| typename std::remove_reference<Result>::type>::type>::value) { |
| m_result_recorded = false; |
| } else { |
| serializer.SerializeAll(sequence); |
| serializer.SerializeAll(0); |
| m_result_recorded = true; |
| } |
| } |
| |
| /// Records a single function call. |
| template <typename... Args> |
| void Record(Serializer &serializer, Registry ®istry, void (*f)(Args...), |
| const Args &... args) { |
| m_serializer = &serializer; |
| if (!ShouldCapture()) |
| return; |
| |
| std::lock_guard<std::mutex> lock(g_mutex); |
| unsigned sequence = GetSequenceNumber(); |
| unsigned id = registry.GetID(uintptr_t(f)); |
| |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| Log(id); |
| #endif |
| |
| serializer.SerializeAll(sequence); |
| serializer.SerializeAll(id); |
| serializer.SerializeAll(args...); |
| |
| // Record result. |
| serializer.SerializeAll(sequence); |
| serializer.SerializeAll(0); |
| m_result_recorded = true; |
| } |
| |
| /// Specializations for the no-argument methods. These are passed an empty |
| /// dummy argument so the same variadic macro can be used. These methods |
| /// strip the arguments before forwarding them. |
| template <typename Result> |
| void Record(Serializer &serializer, Registry ®istry, Result (*f)(), |
| const EmptyArg &arg) { |
| Record(serializer, registry, f); |
| } |
| |
| /// Record the result of a function call. |
| template <typename Result> |
| Result RecordResult(Result &&r, bool update_boundary) { |
| // When recording the result from the LLDB_RECORD_RESULT macro, we need to |
| // update the boundary so we capture the copy constructor. However, when |
| // called to record the this pointer of the (copy) constructor, the |
| // boundary should not be toggled, because it is called from the |
| // LLDB_RECORD_CONSTRUCTOR macro, which might be followed by other API |
| // calls. |
| if (update_boundary) |
| UpdateBoundary(); |
| if (m_serializer && ShouldCapture()) { |
| std::lock_guard<std::mutex> lock(g_mutex); |
| assert(!m_result_recorded); |
| m_serializer->SerializeAll(GetSequenceNumber()); |
| m_serializer->SerializeAll(r); |
| m_result_recorded = true; |
| } |
| return std::forward<Result>(r); |
| } |
| |
| template <typename Result, typename T> |
| Result Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr, |
| bool update_boundary) { |
| deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>()); |
| unsigned actual_id = registry.GetID(addr); |
| unsigned id = deserializer.Deserialize<unsigned>(); |
| registry.CheckID(id, actual_id); |
| return ReplayResult<Result>( |
| static_cast<DefaultReplayer<T> *>(registry.GetReplayer(id)) |
| ->Replay(deserializer), |
| update_boundary); |
| } |
| |
| void Replay(Deserializer &deserializer, Registry ®istry, uintptr_t addr) { |
| deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>()); |
| unsigned actual_id = registry.GetID(addr); |
| unsigned id = deserializer.Deserialize<unsigned>(); |
| registry.CheckID(id, actual_id); |
| registry.GetReplayer(id)->operator()(deserializer); |
| } |
| |
| 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; } |
| |
| /// Mark the current thread as a private thread and pretend that everything |
| /// on this thread is behind happening behind the API boundary. |
| static void PrivateThread() { g_global_boundary = true; } |
| |
| private: |
| static unsigned GetNextSequenceNumber() { return g_sequence++; } |
| unsigned GetSequenceNumber() const; |
| |
| template <typename T> friend struct replay; |
| void UpdateBoundary() { |
| if (m_local_boundary) |
| g_global_boundary = false; |
| } |
| |
| #ifdef LLDB_REPRO_INSTR_TRACE |
| void Log(unsigned id) { |
| this_thread_id() << "Recording " << id << ": " << m_pretty_func << " (" |
| << m_pretty_args << ")\n"; |
| } |
| #endif |
| |
| Serializer *m_serializer; |
| |
| /// Pretty function for logging. |
| llvm::StringRef m_pretty_func; |
| std::string m_pretty_args; |
| |
| /// Whether this function call was the one crossing the API boundary. |
| bool m_local_boundary; |
| |
| /// Whether the return value was recorded explicitly. |
| bool m_result_recorded; |
| |
| /// The sequence number for this pair of function and result. |
| unsigned m_sequence; |
| |
| /// Whether we're currently across the API boundary. |
| static thread_local bool g_global_boundary; |
| |
| /// Global mutex to protect concurrent access. |
| static std::mutex g_mutex; |
| |
| /// Unique, monotonically increasing sequence number. |
| static std::atomic<unsigned> g_sequence; |
| }; |
| |
| /// 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, 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<Class *, Class *(Args...)>( |
| 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 <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) { |
| return recorder.Replay<Result, Result(Class *, Args...)>( |
| deserializer, registry, uintptr_t(&record), 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) { |
| recorder.Replay(deserializer, registry, uintptr_t(&record)); |
| } |
| }; |
| }; |
| |
| template <typename Result, typename Class, typename... Args> |
| struct invoke<Result (Class::*)(Args...) const> { |
| template <Result (Class::*m)(Args...) const> 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<Result, Result(Class *, Args...)>( |
| deserializer, registry, uintptr_t(&record), true); |
| } |
| }; |
| }; |
| |
| template <typename Class, typename... Args> |
| struct invoke<void (Class::*)(Args...) const> { |
| template <void (Class::*m)(Args...) const> 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 <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 Result record(Args... args) { return (*m)(args...); } |
| static Result replay(Recorder &recorder, Deserializer &deserializer, |
| Registry ®istry) { |
| return recorder.Replay<Result, Result(Args...)>(deserializer, registry, |
| uintptr_t(&record), true); |
| } |
| }; |
| }; |
| |
| template <typename... Args> struct invoke<void (*)(Args...)> { |
| template <void (*m)(Args...)> 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)); |
| } |
| }; |
| }; |
| |
| /// Special handling for functions returning strings as (char*, size_t). |
| /// { |
| |
| /// For inline replay, we ignore the arguments and use the ones from the |
| /// serializer instead. This doesn't work for methods that use a char* and a |
| /// size to return a string. For one these functions have a custom replayer to |
| /// prevent override the input buffer. Furthermore, the template-generated |
| /// deserialization is not easy to hook into. |
| /// |
| /// The specializations below hand-implement the serialization logic for the |
| /// inline replay. Instead of using the function from the registry, it uses the |
| /// one passed into the macro. |
| template <typename Signature> struct invoke_char_ptr; |
| template <typename Result, typename Class, typename... Args> |
| struct invoke_char_ptr<Result (Class::*)(Args...) const> { |
| template <Result (Class::*m)(Args...) const> struct method { |
| static Result record(Class *c, char *s, size_t l) { |
| char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char))); |
| return (c->*m)(buffer, l); |
| } |
| |
| static Result replay(Recorder &recorder, Deserializer &deserializer, |
| Registry ®istry, char *str) { |
| deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>()); |
| deserializer.Deserialize<unsigned>(); |
| Class *c = deserializer.Deserialize<Class *>(); |
| deserializer.Deserialize<const char *>(); |
| size_t l = deserializer.Deserialize<size_t>(); |
| return recorder.ReplayResult( |
| std::move(deserializer.HandleReplayResult((c->*m)(str, l))), true); |
| } |
| }; |
| }; |
| |
| template <typename Signature> struct invoke_char_ptr; |
| template <typename Result, typename Class, typename... Args> |
| struct invoke_char_ptr<Result (Class::*)(Args...)> { |
| template <Result (Class::*m)(Args...)> struct method { |
| static Result record(Class *c, char *s, size_t l) { |
| char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char))); |
| return (c->*m)(buffer, l); |
| } |
| |
| static Result replay(Recorder &recorder, Deserializer &deserializer, |
| Registry ®istry, char *str) { |
| deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>()); |
| deserializer.Deserialize<unsigned>(); |
| Class *c = deserializer.Deserialize<Class *>(); |
| deserializer.Deserialize<const char *>(); |
| size_t l = deserializer.Deserialize<size_t>(); |
| return recorder.ReplayResult( |
| std::move(deserializer.HandleReplayResult((c->*m)(str, l))), true); |
| } |
| }; |
| }; |
| |
| template <typename Result, typename... Args> |
| struct invoke_char_ptr<Result (*)(Args...)> { |
| template <Result (*m)(Args...)> struct method { |
| static Result record(char *s, size_t l) { |
| char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char))); |
| return (*m)(buffer, l); |
| } |
| |
| static Result replay(Recorder &recorder, Deserializer &deserializer, |
| Registry ®istry, char *str) { |
| deserializer.SetExpectedSequence(deserializer.Deserialize<unsigned>()); |
| deserializer.Deserialize<unsigned>(); |
| deserializer.Deserialize<const char *>(); |
| size_t l = deserializer.Deserialize<size_t>(); |
| return recorder.ReplayResult( |
| std::move(deserializer.HandleReplayResult((*m)(str, l))), true); |
| } |
| }; |
| }; |
| /// } |
| |
| } // namespace repro |
| } // namespace lldb_private |
| |
| #endif // LLDB_UTILITY_REPRODUCERINSTRUMENTATION_H |