[Lldb-commits] [lldb] 950a8aa - [lldb/Reproducers] Support new replay mode: passive replay

Jonas Devlieghere via lldb-commits lldb-commits at lists.llvm.org
Mon Apr 20 09:45:58 PDT 2020


Author: Jonas Devlieghere
Date: 2020-04-20T09:41:40-07:00
New Revision: 950a8aa165ebe7c7b8b8b83b2c6c60af5fad89d7

URL: https://github.com/llvm/llvm-project/commit/950a8aa165ebe7c7b8b8b83b2c6c60af5fad89d7
DIFF: https://github.com/llvm/llvm-project/commit/950a8aa165ebe7c7b8b8b83b2c6c60af5fad89d7.diff

LOG: [lldb/Reproducers] Support new replay mode: passive replay

Support passive replay as proposed in the RFC [1] on lldb-dev and
described in more detail on the lldb website [2].

This patch extends the LLDB_RECORD macros to re-invoke the current
function with arguments deserialized from the reproducer. This relies on
the function being called in the exact same order as during replay. It
uses the same mechanism to toggle the API boundary as during recording,
which guarantees that only boundary crossing calls are replayed.

Another major change is that before this patch we could ignore the
result of an API call, because we only cared about the observable
behavior. Now we need to be able to return the replayed result to the
SWIG bindings.

We reuse a lot of the recording infrastructure, which can be a little
confusing. We kept the existing naming to limit the amount of churn, but
might revisit that in a future patch.

[1] http://lists.llvm.org/pipermail/lldb-dev/2020-April/016100.html
[2] https://lldb.llvm.org/resources/reproducers.html

Differential revision: https://reviews.llvm.org/D77602

Added: 
    

Modified: 
    lldb/include/lldb/Utility/Reproducer.h
    lldb/include/lldb/Utility/ReproducerInstrumentation.h
    lldb/source/API/SBDebugger.cpp
    lldb/source/API/SBReproducer.cpp
    lldb/source/API/SBReproducerPrivate.h
    lldb/source/Utility/Reproducer.cpp
    lldb/source/Utility/ReproducerInstrumentation.cpp
    lldb/unittests/Utility/ReproducerInstrumentationTest.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Utility/Reproducer.h b/lldb/include/lldb/Utility/Reproducer.h
index 10d2c801bc95..499243b41202 100644
--- a/lldb/include/lldb/Utility/Reproducer.h
+++ b/lldb/include/lldb/Utility/Reproducer.h
@@ -27,6 +27,7 @@ class Reproducer;
 enum class ReproducerMode {
   Capture,
   Replay,
+  PassiveReplay,
   Off,
 };
 
@@ -287,7 +288,7 @@ class Generator final {
 
 class Loader final {
 public:
-  Loader(FileSpec root);
+  Loader(FileSpec root, bool passive = false);
 
   template <typename T> FileSpec GetFile() {
     if (!HasFile(T::file))
@@ -309,12 +310,15 @@ class Loader final {
 
   const FileSpec &GetRoot() const { return m_root; }
 
+  bool IsPassiveReplay() const { return m_passive_replay; }
+
 private:
   bool HasFile(llvm::StringRef file);
 
   FileSpec m_root;
   std::vector<std::string> m_files;
   bool m_loaded;
+  bool m_passive_replay;
 };
 
 /// The reproducer enables clients to obtain access to the Generator and
@@ -342,7 +346,7 @@ class Reproducer {
 
 protected:
   llvm::Error SetCapture(llvm::Optional<FileSpec> root);
-  llvm::Error SetReplay(llvm::Optional<FileSpec> root);
+  llvm::Error SetReplay(llvm::Optional<FileSpec> root, bool passive = false);
 
 private:
   static llvm::Optional<Reproducer> &InstanceImpl();

diff  --git a/lldb/include/lldb/Utility/ReproducerInstrumentation.h b/lldb/include/lldb/Utility/ReproducerInstrumentation.h
index 3b5dde3d2e2a..1e027c3952fe 100644
--- a/lldb/include/lldb/Utility/ReproducerInstrumentation.h
+++ b/lldb/include/lldb/Utility/ReproducerInstrumentation.h
@@ -80,68 +80,72 @@ template <typename... Ts> inline std::string stringify_args(const Ts &... ts) {
 // #define LLDB_REPRO_INSTR_TRACE
 
 #define LLDB_REGISTER_CONSTRUCTOR(Class, Signature)                            \
-  R.Register<Class * Signature>(&construct<Class Signature>::doit, "", #Class, \
-                                #Class, #Signature)
+  R.Register<Class * Signature>(&construct<Class Signature>::record, "",       \
+                                #Class, #Class, #Signature)
 
 #define LLDB_REGISTER_METHOD(Result, Class, Method, Signature)                 \
   R.Register(                                                                  \
-      &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::doit,     \
+      &invoke<Result(Class::*) Signature>::method<(&Class::Method)>::record,   \
       #Result, #Class, #Method, #Signature)
 
 #define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature)           \
   R.Register(&invoke<Result(Class::*)                                          \
-                         Signature const>::method<(&Class::Method)>::doit,     \
+                         Signature const>::method<(&Class::Method)>::record,   \
              #Result, #Class, #Method, #Signature)
 
 #define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature)          \
-  R.Register(&invoke<Result(*) Signature>::method<(&Class::Method)>::doit,     \
+  R.Register(&invoke<Result(*) Signature>::method<(&Class::Method)>::record,   \
              #Result, #Class, #Method, #Signature)
 
 #define LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(Result, Class, Method)          \
   R.Register(                                                                  \
-      &invoke<Result (*)(char *, size_t)>::method<(&Class::Method)>::doit,     \
-      &char_ptr_redirect<Result (*)(char *,                                    \
-                                    size_t)>::method<(&Class::Method)>::doit,  \
+      &invoke<Result (*)(char *, size_t)>::method<(&Class::Method)>::record,   \
+      &char_ptr_redirect<Result (*)(char *, size_t)>::method<(                 \
+          &Class::Method)>::record,                                            \
       #Result, #Class, #Method, "(char*, size_t");
 
 #define LLDB_REGISTER_CHAR_PTR_REDIRECT(Result, Class, Method)                 \
   R.Register(&invoke<Result (Class::*)(char *, size_t)>::method<(              \
-                 &Class::Method)>::doit,                                       \
+                 &Class::Method)>::record,                                     \
              &char_ptr_redirect<Result (Class::*)(char *, size_t)>::method<(   \
-                 &Class::Method)>::doit,                                       \
+                 &Class::Method)>::record,                                     \
              #Result, #Class, #Method, "(char*, size_t");
 
 #define LLDB_REGISTER_CHAR_PTR_REDIRECT_CONST(Result, Class, Method)           \
   R.Register(&invoke<Result (Class::*)(char *, size_t)                         \
-                         const>::method<(&Class::Method)>::doit,               \
+                         const>::method<(&Class::Method)>::record,             \
              &char_ptr_redirect<Result (Class::*)(char *, size_t)              \
-                                    const>::method<(&Class::Method)>::doit,    \
+                                    const>::method<(&Class::Method)>::record,  \
              #Result, #Class, #Method, "(char*, size_t");
 
-#define LLDB_CONSTRUCT_(T, ...)                                                \
-  lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
-                                          stringify_args(this, __VA_ARGS__));  \
-  if (lldb_private::repro::InstrumentationData _data =                         \
-          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \
-    _recorder.Record(_data.GetSerializer(), _data.GetRegistry(),               \
-                     &lldb_private::repro::construct<T>::doit, __VA_ARGS__);   \
-    _recorder.RecordResult(this, false);                                       \
-  }
+#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, __VA_ARGS__)
+  LLDB_CONSTRUCT_(Class Signature, this, __VA_ARGS__)
 
 #define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class)                                 \
-  LLDB_CONSTRUCT_(Class(), lldb_private::repro::EmptyArg())
+  LLDB_CONSTRUCT_(Class(), this, lldb_private::repro::EmptyArg())
 
 #define LLDB_RECORD_(T1, T2, ...)                                              \
   lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
                                           stringify_args(__VA_ARGS__));        \
   if (lldb_private::repro::InstrumentationData _data =                         \
           LLDB_GET_INSTRUMENTATION_DATA()) {                                   \
-    _recorder.Record(_data.GetSerializer(), _data.GetRegistry(),               \
-                     &lldb_private::repro::invoke<T1>::method<T2>::doit,       \
-                     __VA_ARGS__);                                             \
+    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, ...)              \
@@ -171,6 +175,7 @@ template <typename... Ts> inline std::string stringify_args(const Ts &... ts) {
 #define LLDB_RECORD_DUMMY(Result, Class, Method, Signature, ...)               \
   lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION,                \
                                           stringify_args(__VA_ARGS__));
+
 #define LLDB_RECORD_DUMMY_NO_ARGS(Result, Class, Method)                       \
   lldb_private::repro::Recorder _recorder(LLVM_PRETTY_FUNCTION);
 
@@ -204,17 +209,19 @@ class IndexToObject {
   }
 
   /// Adds a pointer to an object to the mapping for the given index.
-  template <typename T> void AddObjectForIndex(unsigned idx, T *object) {
+  template <typename T> T *AddObjectForIndex(unsigned idx, T *object) {
     AddObjectForIndexImpl(
         idx, static_cast<void *>(
                  const_cast<typename std::remove_const<T>::type *>(object)));
+    return object;
   }
 
   /// Adds a reference to an object to the mapping for the given index.
-  template <typename T> void AddObjectForIndex(unsigned idx, T &object) {
+  template <typename T> T &AddObjectForIndex(unsigned idx, T &object) {
     AddObjectForIndexImpl(
         idx, static_cast<void *>(
                  const_cast<typename std::remove_const<T>::type *>(&object)));
+    return object;
   }
 
   /// Get all objects sorted by their index.
@@ -285,21 +292,29 @@ class Deserializer {
     return t;
   }
 
+  template <typename T> const T &HandleReplayResult(const T &t) {
+    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> void HandleReplayResult(const T &t) {
+  template <typename T> T &HandleReplayResult(T &t) {
     unsigned result = Deserialize<unsigned>();
     if (is_trivially_serializable<T>::value)
-      return;
+      return t;
     // We need to make a copy as the original object might go out of scope.
-    m_index_to_object.AddObjectForIndex(result, new T(t));
+    return *m_index_to_object.AddObjectForIndex(result, new T(t));
   }
 
   /// Store the returned value in the index-to-object mapping.
-  template <typename T> void HandleReplayResult(T *t) {
+  template <typename T> T *HandleReplayResult(T *t) {
     unsigned result = Deserialize<unsigned>();
     if (is_trivially_serializable<T>::value)
-      return;
-    m_index_to_object.AddObjectForIndex(result, t);
+      return t;
+    return m_index_to_object.AddObjectForIndex(result, t);
   }
 
   /// All returned types are recorded, even when the function returns a void.
@@ -411,7 +426,11 @@ struct DefaultReplayer<Result(Args...)> : public Replayer {
   DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {}
 
   void operator()(Deserializer &deserializer) const override {
-    deserializer.HandleReplayResult(
+    Replay(deserializer);
+  }
+
+  Result Replay(Deserializer &deserializer) const {
+    return deserializer.HandleReplayResult(
         DeserializationHelper<Args...>::template deserialized<Result>::doit(
             deserializer, f));
   }
@@ -426,6 +445,10 @@ 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();
@@ -487,15 +510,19 @@ class Registry {
   /// Returns the ID for a given function address.
   unsigned GetID(uintptr_t addr);
 
+  /// Get the replayer matching the given ID.
+  Replayer *GetReplayer(unsigned id);
+
+  std::string GetSignature(unsigned id);
+
+  void CheckID(unsigned expected, unsigned actual);
+
 protected:
   /// Register the given replayer for a function (and the ID mapping).
   void DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
                   SignatureStr signature);
 
 private:
-  std::string GetSignature(unsigned id);
-  Replayer *GetReplayer(unsigned id);
-
   /// Mapping of function addresses to replayers and their ID.
   std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>>
       m_replayers;
@@ -504,50 +531,6 @@ class Registry {
   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 {
-    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
-  };
-};
-
-template <typename Result, typename... Args>
-struct invoke<Result (*)(Args...)> {
-  template <Result (*m)(Args...)> struct method {
-    static Result doit(Args... args) { return (*m)(args...); }
-  };
-};
-
-template <typename... Args> struct invoke<void (*)(Args...)> {
-  template <void (*m)(Args...)> struct method {
-    static void doit(Args... args) { return (*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.
 ///
@@ -656,21 +639,41 @@ class Serializer {
 
   /// Mapping of objects to indices.
   ObjectToIndex m_tracker;
-};
+}; // namespace repro
 
 class InstrumentationData {
 public:
-  InstrumentationData() : m_serializer(nullptr), m_registry(nullptr){};
-  InstrumentationData(Serializer &serializer, Registry &registry)
-      : m_serializer(&serializer), m_registry(&registry){};
-
-  Serializer &GetSerializer() { return *m_serializer; }
+  Serializer *GetSerializer() { return m_serializer; }
+  Deserializer *GetDeserializer() { return m_deserializer; }
   Registry &GetRegistry() { return *m_registry; }
 
-  operator bool() { return m_serializer != nullptr && m_registry != nullptr; }
+  operator bool() {
+    return (m_serializer != nullptr || m_deserializer != nullptr) &&
+           m_registry != nullptr;
+  }
+
+  static void Initialize(Serializer &serializer, Registry &registry);
+  static void Initialize(Deserializer &serializer, Registry &registry);
+  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 &registry)
+      : m_serializer(&serializer), m_deserializer(nullptr),
+        m_registry(&registry) {}
+  InstrumentationData(Deserializer &deserializer, Registry &registry)
+      : m_serializer(nullptr), m_deserializer(&deserializer),
+        m_registry(&registry) {}
 
 private:
+  static llvm::Optional<InstrumentationData> &InstanceImpl();
+
   Serializer *m_serializer;
+  Deserializer *m_deserializer;
   Registry *m_registry;
 };
 
@@ -769,14 +772,41 @@ class Recorder {
     return std::forward<Result>(r);
   }
 
+  template <typename Result, typename T>
+  Result Replay(Deserializer &deserializer, Registry &registry, uintptr_t addr,
+                bool update_boundary) {
+    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 &registry, uintptr_t addr) {
+    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; }
+
 private:
+  template <typename T> friend struct replay;
   void UpdateBoundary() {
     if (m_local_boundary)
       g_global_boundary = false;
   }
 
-  bool ShouldCapture() { return m_local_boundary; }
-
 #ifdef LLDB_REPRO_INSTR_TRACE
   void Log(unsigned id) {
     llvm::errs() << "Recording " << id << ": " << m_pretty_func << " ("
@@ -800,11 +830,127 @@ class Recorder {
   static bool g_global_boundary;
 };
 
+/// To be used as the "Runtime ID" of a constructor. It also invokes the
+/// constructor when called.
+template <typename Signature> struct construct;
+template <typename Class, typename... Args> struct construct<Class(Args...)> {
+  static Class *handle(lldb_private::repro::InstrumentationData data,
+                       lldb_private::repro::Recorder &recorder, 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 &registry) {
+    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 &registry) {
+      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 &registry) {
+      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 &registry) {
+      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 &registry) {
+      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 &registry) {
+      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 &registry) {
+      recorder.Replay(deserializer, registry, uintptr_t(&record));
+    }
+  };
+};
+
 template <typename Signature> struct char_ptr_redirect;
 template <typename Result, typename Class>
 struct char_ptr_redirect<Result (Class::*)(char *, size_t) const> {
   template <Result (Class::*m)(char *, size_t) const> struct method {
-    static Result doit(Class *c, char *s, size_t l) {
+    static Result record(Class *c, char *s, size_t l) {
       char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char)));
       return (c->*m)(buffer, l);
     }
@@ -813,17 +959,16 @@ struct char_ptr_redirect<Result (Class::*)(char *, size_t) const> {
 template <typename Result, typename Class>
 struct char_ptr_redirect<Result (Class::*)(char *, size_t)> {
   template <Result (Class::*m)(char *, size_t)> struct method {
-    static Result doit(Class *c, char *s, size_t l) {
+    static Result record(Class *c, char *s, size_t l) {
       char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char)));
       return (c->*m)(buffer, l);
     }
   };
 };
-
 template <typename Result>
 struct char_ptr_redirect<Result (*)(char *, size_t)> {
   template <Result (*m)(char *, size_t)> struct method {
-    static Result doit(char *s, size_t l) {
+    static Result record(char *s, size_t l) {
       char *buffer = reinterpret_cast<char *>(calloc(l, sizeof(char)));
       return (*m)(buffer, l);
     }

diff  --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index 414d85e64363..5654ec59b4fd 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -1641,31 +1641,31 @@ static SBError SetFileRedirect(SBDebugger *, FileSP file) { return SBError(); }
 
 template <> void RegisterMethods<SBDebugger>(Registry &R) {
   // Custom implementation.
-  R.Register(&invoke<void (SBDebugger::*)(
-                 FILE *, bool)>::method<&SBDebugger::SetErrorFileHandle>::doit,
+  R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method<
+                 &SBDebugger::SetErrorFileHandle>::record,
              &SetFileHandleRedirect);
-  R.Register(&invoke<void (SBDebugger::*)(
-                 FILE *, bool)>::method<&SBDebugger::SetOutputFileHandle>::doit,
+  R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method<
+                 &SBDebugger::SetOutputFileHandle>::record,
              &SetFileHandleRedirect);
 
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 SBFile)>::method<&SBDebugger::SetInputFile>::doit,
+                 SBFile)>::method<&SBDebugger::SetInputFile>::record,
              &SetFileRedirect);
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 SBFile)>::method<&SBDebugger::SetOutputFile>::doit,
+                 SBFile)>::method<&SBDebugger::SetOutputFile>::record,
              &SetFileRedirect);
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 SBFile)>::method<&SBDebugger::SetErrorFile>::doit,
+                 SBFile)>::method<&SBDebugger::SetErrorFile>::record,
              &SetFileRedirect);
 
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 FileSP)>::method<&SBDebugger::SetInputFile>::doit,
+                 FileSP)>::method<&SBDebugger::SetInputFile>::record,
              &SetFileRedirect);
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 FileSP)>::method<&SBDebugger::SetOutputFile>::doit,
+                 FileSP)>::method<&SBDebugger::SetOutputFile>::record,
              &SetFileRedirect);
   R.Register(&invoke<SBError (SBDebugger::*)(
-                 FileSP)>::method<&SBDebugger::SetErrorFile>::doit,
+                 FileSP)>::method<&SBDebugger::SetErrorFile>::record,
              &SetFileRedirect);
 
   LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(bool, SBDebugger,

diff  --git a/lldb/source/API/SBReproducer.cpp b/lldb/source/API/SBReproducer.cpp
index ba9127962ebd..56c586b6d239 100644
--- a/lldb/source/API/SBReproducer.cpp
+++ b/lldb/source/API/SBReproducer.cpp
@@ -111,6 +111,12 @@ const char *SBReproducer::Capture() {
     error = llvm::toString(std::move(e));
     return error.c_str();
   }
+
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
+    auto &p = g->GetOrCreate<SBProvider>();
+    InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry());
+  }
+
   return nullptr;
 }
 
@@ -121,15 +127,35 @@ const char *SBReproducer::Capture(const char *path) {
     error = llvm::toString(std::move(e));
     return error.c_str();
   }
+
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
+    auto &p = g->GetOrCreate<SBProvider>();
+    InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry());
+  }
+
   return nullptr;
 }
 
 const char *SBReproducer::PassiveReplay(const char *path) {
   static std::string error;
-  if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
+  if (auto e = Reproducer::Initialize(ReproducerMode::PassiveReplay,
+                                      FileSpec(path))) {
     error = llvm::toString(std::move(e));
     return error.c_str();
   }
+
+  if (auto *l = lldb_private::repro::Reproducer::Instance().GetLoader()) {
+    FileSpec file = l->GetFile<SBProvider::Info>();
+    auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+    if (!error_or_file) {
+      error =
+          "unable to read SB API data: " + error_or_file.getError().message();
+      return error.c_str();
+    }
+    static ReplayData r(std::move(*error_or_file));
+    InstrumentationData::Initialize(r.GetDeserializer(), r.GetRegistry());
+  }
+
   return nullptr;
 }
 

diff  --git a/lldb/source/API/SBReproducerPrivate.h b/lldb/source/API/SBReproducerPrivate.h
index 7ea5512d9fff..a4c6eb94627b 100644
--- a/lldb/source/API/SBReproducerPrivate.h
+++ b/lldb/source/API/SBReproducerPrivate.h
@@ -20,7 +20,7 @@
 #include "llvm/ADT/DenseMap.h"
 
 #define LLDB_GET_INSTRUMENTATION_DATA()                                        \
-  lldb_private::repro::GetInstrumentationData()
+  lldb_private::repro::InstrumentationData::Instance()
 
 namespace lldb_private {
 namespace repro {
@@ -55,17 +55,19 @@ class SBProvider : public Provider<SBProvider> {
   SBRegistry m_registry;
 };
 
-inline InstrumentationData GetInstrumentationData() {
-  if (!lldb_private::repro::Reproducer::Initialized())
-    return {};
-
-  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
-    auto &p = g->GetOrCreate<SBProvider>();
-    return {p.GetSerializer(), p.GetRegistry()};
-  }
+class ReplayData {
+public:
+  ReplayData(std::unique_ptr<llvm::MemoryBuffer> memory_buffer)
+      : m_memory_buffer(std::move(memory_buffer)), m_registry(),
+        m_deserializer(m_memory_buffer->getBuffer()) {}
+  Deserializer &GetDeserializer() { return m_deserializer; }
+  Registry &GetRegistry() { return m_registry; }
 
-  return {};
-}
+private:
+  std::unique_ptr<llvm::MemoryBuffer> m_memory_buffer;
+  SBRegistry m_registry;
+  Deserializer m_deserializer;
+};
 
 template <typename T> void RegisterMethods(Registry &R);
 

diff  --git a/lldb/source/Utility/Reproducer.cpp b/lldb/source/Utility/Reproducer.cpp
index bbf399e599bf..eb7d96baab57 100644
--- a/lldb/source/Utility/Reproducer.cpp
+++ b/lldb/source/Utility/Reproducer.cpp
@@ -62,7 +62,9 @@ llvm::Error Reproducer::Initialize(ReproducerMode mode,
     return Instance().SetCapture(root);
   } break;
   case ReproducerMode::Replay:
-    return Instance().SetReplay(root);
+    return Instance().SetReplay(root, /*passive*/ false);
+  case ReproducerMode::PassiveReplay:
+    return Instance().SetReplay(root, /*passive*/ true);
   case ReproducerMode::Off:
     break;
   };
@@ -127,7 +129,7 @@ llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
   return Error::success();
 }
 
-llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
+llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) {
   std::lock_guard<std::mutex> guard(m_mutex);
 
   if (root && m_generator)
@@ -140,7 +142,7 @@ llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
     return Error::success();
   }
 
-  m_loader.emplace(*root);
+  m_loader.emplace(*root, passive);
   if (auto e = m_loader->LoadIndex())
     return e;
 
@@ -227,8 +229,9 @@ void Generator::AddProvidersToIndex() {
   yout << files;
 }
 
-Loader::Loader(FileSpec root)
-    : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {}
+Loader::Loader(FileSpec root, bool passive)
+    : m_root(MakeAbsolute(std::move(root))), m_loaded(false),
+      m_passive_replay(passive) {}
 
 llvm::Error Loader::LoadIndex() {
   if (m_loaded)

diff  --git a/lldb/source/Utility/ReproducerInstrumentation.cpp b/lldb/source/Utility/ReproducerInstrumentation.cpp
index ec66da9973ae..46bf6b76e1d2 100644
--- a/lldb/source/Utility/ReproducerInstrumentation.cpp
+++ b/lldb/source/Utility/ReproducerInstrumentation.cpp
@@ -8,9 +8,9 @@
 
 #include "lldb/Utility/ReproducerInstrumentation.h"
 #include "lldb/Utility/Reproducer.h"
-#include <thread>
 #include <stdio.h>
 #include <stdlib.h>
+#include <thread>
 
 using namespace lldb_private;
 using namespace lldb_private::repro;
@@ -120,7 +120,7 @@ bool Registry::Replay(Deserializer &deserializer) {
 
   // Add a small artificial delay to ensure that all asynchronous events have
   // completed before we exit.
-  std::this_thread::sleep_for (std::chrono::milliseconds(100));
+  std::this_thread::sleep_for(std::chrono::milliseconds(100));
 
   return true;
 }
@@ -145,6 +145,22 @@ std::string Registry::GetSignature(unsigned id) {
   return m_ids[id].second.ToString();
 }
 
+void Registry::CheckID(unsigned expected, unsigned actual) {
+  if (expected != actual) {
+    llvm::errs() << "Reproducer expected signature " << expected << ": '"
+                 << GetSignature(expected) << "'\n";
+    llvm::errs() << "Reproducer actual signature " << actual << ": '"
+                 << GetSignature(actual) << "'\n";
+    llvm::report_fatal_error(
+        "Detected reproducer replay divergence. Refusing to continue.");
+  }
+
+#ifdef LLDB_REPRO_INSTR_TRACE
+  llvm::errs() << "Replaying " << actual << ": " << GetSignature(actual)
+               << "\n";
+#endif
+}
+
 Replayer *Registry::GetReplayer(unsigned id) {
   assert(m_ids.count(id) != 0 && "ID not in registry");
   return m_ids[id].first;
@@ -181,4 +197,25 @@ Recorder::~Recorder() {
   UpdateBoundary();
 }
 
+void InstrumentationData::Initialize(Serializer &serializer,
+                                     Registry &registry) {
+  InstanceImpl().emplace(serializer, registry);
+}
+
+void InstrumentationData::Initialize(Deserializer &deserializer,
+                                     Registry &registry) {
+  InstanceImpl().emplace(deserializer, registry);
+}
+
+InstrumentationData &InstrumentationData::Instance() {
+  if (!InstanceImpl())
+    InstanceImpl().emplace();
+  return *InstanceImpl();
+}
+
+llvm::Optional<InstrumentationData> &InstrumentationData::InstanceImpl() {
+  static llvm::Optional<InstrumentationData> g_instrumentation_data;
+  return g_instrumentation_data;
+}
+
 bool lldb_private::repro::Recorder::g_global_boundary;

diff  --git a/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp b/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp
index b17a8f169681..1a7f21ded9a5 100644
--- a/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp
+++ b/lldb/unittests/Utility/ReproducerInstrumentationTest.cpp
@@ -52,23 +52,62 @@ class TestingRegistry : public Registry {
 
 static llvm::Optional<TestingRegistry> g_registry;
 static llvm::Optional<Serializer> g_serializer;
+static llvm::Optional<Deserializer> g_deserializer;
 
-inline InstrumentationData GetTestInstrumentationData() {
+class TestInstrumentationData : public InstrumentationData {
+public:
+  TestInstrumentationData() : InstrumentationData() {}
+  TestInstrumentationData(Serializer &serializer, Registry &registry)
+      : InstrumentationData(serializer, registry) {}
+  TestInstrumentationData(Deserializer &deserializer, Registry &registry)
+      : InstrumentationData(deserializer, registry) {}
+};
+
+inline TestInstrumentationData GetTestInstrumentationData() {
+  assert(!(g_serializer && g_deserializer));
   if (g_serializer)
-    return InstrumentationData(*g_serializer, *g_registry);
-  return InstrumentationData();
+    return TestInstrumentationData(*g_serializer, *g_registry);
+  if (g_deserializer)
+    return TestInstrumentationData(*g_deserializer, *g_registry);
+  return TestInstrumentationData();
 }
 
 class TestInstrumentationDataRAII {
-public:
+private:
   TestInstrumentationDataRAII(llvm::raw_string_ostream &os) {
     g_registry.emplace();
     g_serializer.emplace(os);
+    g_deserializer.reset();
   }
 
-  ~TestInstrumentationDataRAII() {
+  TestInstrumentationDataRAII(llvm::StringRef buffer) {
+    g_registry.emplace();
+    g_serializer.reset();
+    g_deserializer.emplace(buffer);
+  }
+
+  friend std::unique_ptr<TestInstrumentationDataRAII>
+  std::make_unique<TestInstrumentationDataRAII>(llvm::raw_string_ostream &os);
+  friend std::unique_ptr<TestInstrumentationDataRAII>
+  std::make_unique<TestInstrumentationDataRAII>(llvm::StringRef &buffer);
+
+public:
+  ~TestInstrumentationDataRAII() { Reset(); }
+
+  void Reset() {
     g_registry.reset();
     g_serializer.reset();
+    g_deserializer.reset();
+  }
+
+  static std::unique_ptr<TestInstrumentationDataRAII>
+  GetRecordingData(llvm::raw_string_ostream &os) {
+    return std::make_unique<TestInstrumentationDataRAII>(os);
+  }
+
+  static std::unique_ptr<TestInstrumentationDataRAII>
+  GetReplayData(llvm::StringRef buffer) {
+    return std::make_unique<TestInstrumentationDataRAII>(buffer);
   }
 };
 
@@ -95,11 +134,17 @@ class InstrumentedFoo : public Instrumented {
   InstrumentedFoo(const InstrumentedFoo &foo);
   InstrumentedFoo &operator=(const InstrumentedFoo &foo);
   void A(int a);
+  int GetA();
   void B(int &b) const;
+  int &GetB();
   int C(float *c);
+  float GetC();
   int D(const char *d) const;
+  void GetD(char *buffer, size_t length);
   static void E(double e);
+  double GetE();
   static int F();
+  bool GetF();
   void Validate() override;
   //// }
   virtual bool IsA(Class c) override { return c == Class::Foo; }
@@ -182,35 +227,71 @@ void InstrumentedFoo::A(int a) {
   m_a = a;
 }
 
+int InstrumentedFoo::GetA() {
+  LLDB_RECORD_METHOD_NO_ARGS(int, InstrumentedFoo, GetA);
+
+  return m_a;
+}
+
 void InstrumentedFoo::B(int &b) const {
   LLDB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b);
   m_called++;
   m_b = b;
 }
 
+int &InstrumentedFoo::GetB() {
+  LLDB_RECORD_METHOD_NO_ARGS(int &, InstrumentedFoo, GetB);
+
+  return m_b;
+}
+
 int InstrumentedFoo::C(float *c) {
   LLDB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c);
   m_c = *c;
   return 1;
 }
 
+float InstrumentedFoo::GetC() {
+  LLDB_RECORD_METHOD_NO_ARGS(float, InstrumentedFoo, GetC);
+
+  return m_c;
+}
+
 int InstrumentedFoo::D(const char *d) const {
   LLDB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d);
   m_d = std::string(d);
   return 2;
 }
 
+void InstrumentedFoo::GetD(char *buffer, size_t length) {
+  LLDB_RECORD_METHOD(void, InstrumentedFoo, GetD, (char *, size_t), buffer,
+                     length);
+  ::snprintf(buffer, length, "%s", m_d.c_str());
+}
+
 void InstrumentedFoo::E(double e) {
   LLDB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e);
   g_e = e;
 }
 
+double InstrumentedFoo::GetE() {
+  LLDB_RECORD_METHOD_NO_ARGS(double, InstrumentedFoo, GetE);
+
+  return g_e;
+}
+
 int InstrumentedFoo::F() {
   LLDB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F);
   g_f = true;
   return 3;
 }
 
+bool InstrumentedFoo::GetF() {
+  LLDB_RECORD_METHOD_NO_ARGS(bool, InstrumentedFoo, GetF);
+
+  return g_f;
+}
+
 void InstrumentedFoo::Validate() {
   LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedFoo, Validate);
   EXPECT_EQ(m_a, 100);
@@ -296,6 +377,12 @@ TestingRegistry::TestingRegistry() {
   LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo,
                        (InstrumentedFoo &));
   LLDB_REGISTER_METHOD(void, InstrumentedBar, Validate, ());
+  LLDB_REGISTER_METHOD(int, InstrumentedFoo, GetA, ());
+  LLDB_REGISTER_METHOD(int &, InstrumentedFoo, GetB, ());
+  LLDB_REGISTER_METHOD(float, InstrumentedFoo, GetC, ());
+  LLDB_REGISTER_METHOD(void, InstrumentedFoo, GetD, (char *, size_t));
+  LLDB_REGISTER_METHOD(double, InstrumentedFoo, GetE, ());
+  LLDB_REGISTER_METHOD(bool, InstrumentedFoo, GetF, ());
 }
 
 static const Pod p;
@@ -534,7 +621,7 @@ TEST(RecordReplayTest, InstrumentedFoo) {
   llvm::raw_string_ostream os(str);
 
   {
-    TestInstrumentationDataRAII data(os);
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
 
     int b = 200;
     float c = 300.3f;
@@ -563,7 +650,7 @@ TEST(RecordReplayTest, InstrumentedFooSameThis) {
   llvm::raw_string_ostream os(str);
 
   {
-    TestInstrumentationDataRAII data(os);
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
 
     int b = 200;
     float c = 300.3f;
@@ -603,7 +690,7 @@ TEST(RecordReplayTest, InstrumentedBar) {
   llvm::raw_string_ostream os(str);
 
   {
-    TestInstrumentationDataRAII data(os);
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
 
     InstrumentedBar bar;
     InstrumentedFoo foo = bar.GetInstrumentedFoo();
@@ -643,7 +730,7 @@ TEST(RecordReplayTest, InstrumentedBarRef) {
   llvm::raw_string_ostream os(str);
 
   {
-    TestInstrumentationDataRAII data(os);
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
 
     InstrumentedBar bar;
     InstrumentedFoo &foo = bar.GetInstrumentedFooRef();
@@ -679,7 +766,7 @@ TEST(RecordReplayTest, InstrumentedBarPtr) {
   llvm::raw_string_ostream os(str);
 
   {
-    TestInstrumentationDataRAII data(os);
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
 
     InstrumentedBar bar;
     InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr());
@@ -709,3 +796,302 @@ TEST(RecordReplayTest, InstrumentedBarPtr) {
       deserializer.GetAllObjects(),
       {{Class::Bar, Validator::valid}, {Class::Foo, Validator::valid}});
 }
+
+TEST(PassiveReplayTest, InstrumentedFoo) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  {
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
+
+    int b = 200;
+    float c = 300.3f;
+    double e = 400.4;
+
+    InstrumentedFoo foo(0);
+    foo.A(100);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("bar");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+  }
+
+  std::string buffer = os.str();
+
+  {
+    auto data = TestInstrumentationDataRAII::GetReplayData(buffer);
+
+    int b = 999;
+    float c = 999.9f;
+    double e = 999.9;
+
+    InstrumentedFoo foo(9);
+    foo.A(999);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("999");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+  }
+}
+
+TEST(PassiveReplayTest, InstrumentedFooInvalid) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  {
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
+
+    int b = 200;
+    float c = 300.3f;
+    double e = 400.4;
+
+    InstrumentedFoo foo(0);
+    foo.A(100);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("bar");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+  }
+
+  std::string buffer = os.str();
+
+  {
+    auto data = TestInstrumentationDataRAII::GetReplayData(buffer);
+
+    int b = 999;
+    float c = 999.9f;
+    double e = 999.9;
+
+    InstrumentedFoo foo(9);
+    foo.A(999);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("999");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    // Detect divergence.
+    EXPECT_DEATH(foo.GetA(), "");
+  }
+}
+
+TEST(PassiveReplayTest, InstrumentedBar) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  {
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
+
+    InstrumentedBar bar;
+    InstrumentedFoo foo = bar.GetInstrumentedFoo();
+
+    int b = 200;
+    float c = 300.3f;
+    double e = 400.4;
+
+    foo.A(100);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("bar");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+
+  std::string buffer = os.str();
+
+  {
+    auto data = TestInstrumentationDataRAII::GetReplayData(buffer);
+
+    InstrumentedBar bar;
+    InstrumentedFoo foo = bar.GetInstrumentedFoo();
+
+    int b = 99;
+    float c = 999.9f;
+    double e = 999.9;
+
+    foo.A(999);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("999");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+}
+
+TEST(PassiveReplayTest, InstrumentedBarRef) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  {
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
+
+    InstrumentedBar bar;
+    InstrumentedFoo &foo = bar.GetInstrumentedFooRef();
+
+    int b = 200;
+    float c = 300.3f;
+    double e = 400.4;
+
+    foo.A(100);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("bar");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+
+  std::string buffer = os.str();
+
+  {
+    auto data = TestInstrumentationDataRAII::GetReplayData(buffer);
+
+    InstrumentedBar bar;
+    InstrumentedFoo &foo = bar.GetInstrumentedFooRef();
+
+    int b = 99;
+    float c = 999.9f;
+    double e = 999.9;
+
+    foo.A(999);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("999");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+}
+
+TEST(PassiveReplayTest, InstrumentedBarPtr) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+
+  {
+    auto data = TestInstrumentationDataRAII::GetRecordingData(os);
+
+    InstrumentedBar bar;
+    InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr());
+
+    int b = 200;
+    float c = 300.3f;
+    double e = 400.4;
+
+    foo.A(100);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("bar");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+
+  std::string buffer = os.str();
+
+  {
+    auto data = TestInstrumentationDataRAII::GetReplayData(buffer);
+
+    InstrumentedBar bar;
+    InstrumentedFoo &foo = *(bar.GetInstrumentedFooPtr());
+
+    int b = 99;
+    float c = 999.9f;
+    double e = 999.9;
+
+    foo.A(999);
+    foo.B(b);
+    foo.C(&c);
+    foo.D("999");
+    InstrumentedFoo::E(e);
+    InstrumentedFoo::F();
+    foo.Validate();
+
+    EXPECT_EQ(foo.GetA(), 100);
+    EXPECT_EQ(foo.GetB(), 200);
+    EXPECT_NEAR(foo.GetC(), 300.3, 0.01);
+    EXPECT_NEAR(foo.GetE(), 400.4, 0.01);
+    EXPECT_EQ(foo.GetF(), true);
+
+    bar.SetInstrumentedFoo(foo);
+    bar.SetInstrumentedFoo(&foo);
+    bar.Validate();
+  }
+}


        


More information about the lldb-commits mailing list