diff options
Diffstat (limited to 'include/lldb/Utility/ReproducerInstrumentation.h')
-rw-r--r-- | include/lldb/Utility/ReproducerInstrumentation.h | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/include/lldb/Utility/ReproducerInstrumentation.h b/include/lldb/Utility/ReproducerInstrumentation.h new file mode 100644 index 0000000000000..f90ce4bc767a3 --- /dev/null +++ b/include/lldb/Utility/ReproducerInstrumentation.h @@ -0,0 +1,717 @@ +//===-- 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_REPRODUCER_INSTRUMENTATION_H +#define LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_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 <iostream> +#include <map> +#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, 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 <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 + +#define LLDB_REGISTER_CONSTRUCTOR(Class, Signature) \ + R.Register<Class * Signature>(&construct<Class Signature>::doit, "", #Class, \ + #Class, #Signature) +#define LLDB_REGISTER_METHOD(Result, Class, Method, Signature) \ + R.Register( \ + &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::doit, \ + #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, \ + #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) + +#define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::construct<Class Signature>::doit, \ + __VA_ARGS__); \ + sb_recorder.RecordResult(this); \ + } + +#define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::construct<Class()>::doit); \ + sb_recorder.RecordResult(this); \ + } + +#define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record( \ + data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::invoke<Result(Class::*) Signature>::method<( \ + &Class::Method)>::doit, \ + this, __VA_ARGS__); \ + } + +#define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record( \ + data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::invoke<Result( \ + Class::*) Signature const>::method_const<(&Class::Method)>::doit, \ + this, __VA_ARGS__); \ + } + +#define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::invoke<Result ( \ + Class::*)()>::method<(&Class::Method)>::doit, \ + this); \ + } + +#define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record( \ + data.GetSerializer(), data.GetRegistry(), \ + &lldb_private::repro::invoke<Result ( \ + Class::*)() const>::method_const<(&Class::Method)>::doit, \ + this); \ + } + +#define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \ + static_cast<Result(*) Signature>(&Class::Method), \ + __VA_ARGS__); \ + } + +#define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method) \ + lldb_private::repro::Recorder sb_recorder(LLVM_PRETTY_FUNCTION); \ + if (lldb_private::repro::InstrumentationData data = \ + LLDB_GET_INSTRUMENTATION_DATA()) { \ + sb_recorder.Record(data.GetSerializer(), data.GetRegistry(), \ + static_cast<Result (*)()>(&Class::Method)); \ + } + +#define LLDB_RECORD_RESULT(Result) sb_recorder.RecordResult(Result); + +/// 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 sb_recorder(LLVM_PRETTY_FUNCTION, \ + stringify_args(__VA_ARGS__)); + +namespace lldb_private { +namespace repro { + +/// 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> void AddObjectForIndex(unsigned idx, T *object) { + AddObjectForIndexImpl( + idx, static_cast<void *>( + const_cast<typename std::remove_const<T>::type *>(object))); + } + + /// Adds a reference to an object to the mapping for the given index. + template <typename T> void AddObjectForIndex(unsigned idx, T &object) { + AddObjectForIndexImpl( + idx, static_cast<void *>( + const_cast<typename std::remove_const<T>::type *>(&object))); + } + +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 ValueTag 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() { +#ifdef LLDB_REPRO_INSTR_TRACE + llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << "\n"; +#endif + return Read<T>(typename serializer_tag<T>::type()); + } + + /// Store the returned value in the index-to-object mapping. + template <typename T> void HandleReplayResult(const T &t) { + unsigned result = Deserialize<unsigned>(); + if (std::is_fundamental<T>::value) + return; + // We need to make a copy as the original object might go out of scope. + 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) { + unsigned result = Deserialize<unsigned>(); + if (std::is_fundamental<T>::value) + 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() { + unsigned result = Deserialize<unsigned>(); + assert(result == 0); + (void)result; + } + +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>())); + } + + /// Mapping of indices to objects. + IndexToObject m_index_to_object; + + /// Buffer containing the serialized data. + llvm::StringRef m_buffer; +}; + +/// 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 <> char *Deserializer::Deserialize<char *>(); + +/// 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 { + 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 { + 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), llvm::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), llvm::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); + + /// Returns the ID for a given function address. + unsigned GetID(uintptr_t addr); + +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; + + /// Mapping of IDs to replayer instances. + 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. +/// +/// 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) { + 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) { + if (std::is_fundamental<T>::value) { + m_stream.write(reinterpret_cast<const char *>(&t), sizeof(T)); + } else { + unsigned idx = m_tracker.GetIndexForObject(&t); + Serialize(idx); + } + } + + void Serialize(void *v) { + // FIXME: Support void* + llvm_unreachable("void* is currently unsupported."); + } + + void Serialize(const char *t) { + m_stream << t; + m_stream.write(0x0); + } + + /// Serialization stream. + llvm::raw_ostream &m_stream; + + /// Mapping of objects to indices. + ObjectToIndex m_tracker; +}; + +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; } + Registry &GetRegistry() { return *m_registry; } + + operator bool() { return m_serializer != nullptr && m_registry != nullptr; } + +private: + Serializer *m_serializer; + Registry *m_registry; +}; + +/// 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(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; + + unsigned id = registry.GetID(uintptr_t(f)); + +#ifdef LLDB_REPRO_INSTR_TRACE + Log(id); +#endif + + 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(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; + + unsigned id = registry.GetID(uintptr_t(f)); + +#ifdef LLDB_REPRO_INSTR_TRACE + Log(id); +#endif + + serializer.SerializeAll(id); + serializer.SerializeAll(args...); + + // Record result. + serializer.SerializeAll(0); + m_result_recorded = true; + } + + /// Record the result of a function call. + template <typename Result> Result RecordResult(Result &&r) { + UpdateBoundary(); + if (m_serializer && ShouldCapture()) { + assert(!m_result_recorded); + m_serializer->SerializeAll(r); + m_result_recorded = true; + } + return std::forward<Result>(r); + } + +private: + 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 << " (" + << 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; + + /// Whether we're currently across the API boundary. + static bool g_global_boundary; +}; + +} // namespace repro +} // namespace lldb_private + +#endif // LLDB_UTILITY_REPRODUCER_INSTRUMENTATION_H |