| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112 |
- //===-- 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
|