<div dir="ltr">Several files in this commit incorrectly use the old file headers from before the relicense. I've fixed them, but please be careful with any other outstanding commits.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Feb 6, 2019 at 10:57 AM Jonas Devlieghere via lldb-commits <<a href="mailto:lldb-commits@lists.llvm.org">lldb-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: jdevlieghere<br>
Date: Wed Feb  6 10:57:42 2019<br>
New Revision: 353324<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=353324&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=353324&view=rev</a><br>
Log:<br>
[Reproducers] SBReproducer framework: Capture & Replay<br>
<br>
This is part two of the reproducer instrumentation framework. It<br>
contains the code to capture and replay function calls. The main user of<br>
this framework will be the SB API layer.<br>
<br>
For all the details refer to the RFC on the mailing list:<br>
<a href="http://lists.llvm.org/pipermail/lldb-dev/2019-January/014530.html" rel="noreferrer" target="_blank">http://lists.llvm.org/pipermail/lldb-dev/2019-January/014530.html</a><br>
<br>
Differential revision: <a href="https://reviews.llvm.org/D56322" rel="noreferrer" target="_blank">https://reviews.llvm.org/D56322</a><br>
<br>
Added:<br>
    lldb/trunk/include/lldb/API/SBReproducer.h<br>
    lldb/trunk/source/API/SBReproducer.cpp<br>
    lldb/trunk/source/API/SBReproducerPrivate.h<br>
Modified:<br>
    lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h<br>
    lldb/trunk/source/API/CMakeLists.txt<br>
    lldb/trunk/source/Utility/ReproducerInstrumentation.cpp<br>
    lldb/trunk/tools/driver/Driver.cpp<br>
    lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp<br>
<br>
Added: lldb/trunk/include/lldb/API/SBReproducer.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBReproducer.h?rev=353324&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/API/SBReproducer.h?rev=353324&view=auto</a><br>
==============================================================================<br>
--- lldb/trunk/include/lldb/API/SBReproducer.h (added)<br>
+++ lldb/trunk/include/lldb/API/SBReproducer.h Wed Feb  6 10:57:42 2019<br>
@@ -0,0 +1,23 @@<br>
+//===-- SBReproducer.h ------------------------------------------*- C++ -*-===//<br>
+//<br>
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br>
+// See <a href="https://llvm.org/LICENSE.txt" rel="noreferrer" target="_blank">https://llvm.org/LICENSE.txt</a> for license information.<br>
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLDB_API_SBREPRODUCER_H<br>
+#define LLDB_API_SBREPRODUCER_H<br>
+<br>
+#include "lldb/lldb-defines.h"<br>
+<br>
+namespace lldb {<br>
+<br>
+class LLDB_API SBReproducer {<br>
+public:<br>
+  static bool Replay();<br>
+};<br>
+<br>
+} // namespace lldb<br>
+<br>
+#endif<br>
<br>
Modified: lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h?rev=353324&r1=353323&r2=353324&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h?rev=353324&r1=353323&r2=353324&view=diff</a><br>
==============================================================================<br>
--- lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h (original)<br>
+++ lldb/trunk/include/lldb/Utility/ReproducerInstrumentation.h Wed Feb  6 10:57:42 2019<br>
@@ -1,5 +1,4 @@<br>
 //===-- ReproducerInstrumentation.h -----------------------------*- C++ -*-===//<br>
-//<br>
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br>
 // See <a href="https://llvm.org/LICENSE.txt" rel="noreferrer" target="_blank">https://llvm.org/LICENSE.txt</a> for license information.<br>
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
@@ -18,7 +17,104 @@<br>
 #include "llvm/Support/ErrorHandling.h"<br>
<br>
 #include <map><br>
-#include <mutex><br>
+<br>
+#define LLDB_REGISTER_CONSTRUCTOR(Class, Signature)                            \<br>
+  Register<Class * Signature>(&construct<Class Signature>::doit)<br>
+#define LLDB_REGISTER_METHOD(Result, Class, Method, Signature)                 \<br>
+  Register(&invoke<Result(Class::*) Signature>::method<&Class::Method>::doit)<br>
+#define LLDB_REGISTER_METHOD_CONST(Result, Class, Method, Signature)           \<br>
+  Register(&invoke<Result(Class::*)                                            \<br>
+                       Signature const>::method_const<&Class::Method>::doit)<br>
+#define LLDB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature)          \<br>
+  Register<Result Signature>(static_cast<Result(*) Signature>(&Class::Method))<br>
+<br>
+#define LLDB_RECORD_CONSTRUCTOR(Class, Signature, ...)                         \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    lldb_private::repro::Recorder sb_recorder(                                 \<br>
+        data.GetSerializer(), data.GetRegistry(), LLVM_PRETTY_FUNCTION);       \<br>
+    sb_recorder.Record(&lldb_private::repro::construct<Class Signature>::doit, \<br>
+                       __VA_ARGS__);                                           \<br>
+    sb_recorder.RecordResult(this);                                            \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_CONSTRUCTOR_NO_ARGS(Class)                                 \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    lldb_private::repro::Recorder sb_recorder(                                 \<br>
+        data.GetSerializer(), data.GetRegistry(), LLVM_PRETTY_FUNCTION);       \<br>
+    sb_recorder.Record(&lldb_private::repro::construct<Class()>::doit);        \<br>
+    sb_recorder.RecordResult(this);                                            \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_METHOD(Result, Class, Method, Signature, ...)              \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(                                                       \<br>
+        &lldb_private::repro::invoke<Result(                                   \<br>
+            Class::*) Signature>::method<&Class::Method>::doit,                \<br>
+        this, __VA_ARGS__);                                                    \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...)        \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(                                                       \<br>
+        &lldb_private::repro::invoke<Result(                                   \<br>
+            Class::*) Signature const>::method_const<&Class::Method>::doit,    \<br>
+        this, __VA_ARGS__);                                                    \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_METHOD_NO_ARGS(Result, Class, Method)                      \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(&lldb_private::repro::invoke<Result (                  \<br>
+                            Class::*)()>::method<&Class::Method>::doit,        \<br>
+                        this);                                                 \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method)                \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(                                                       \<br>
+        &lldb_private::repro::invoke<Result (                                  \<br>
+            Class::*)() const>::method_const<&Class::Method>::doit,            \<br>
+        this);                                                                 \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...)       \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(static_cast<Result(*) Signature>(&Class::Method),      \<br>
+                        __VA_ARGS__);                                          \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method)               \<br>
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \<br>
+  if (lldb_private::repro::InstrumentationData data =                          \<br>
+          LLDB_GET_INSTRUMENTATION_DATA()) {                                   \<br>
+    sb_recorder.emplace(data.GetSerializer(), data.GetRegistry(),              \<br>
+                        LLVM_PRETTY_FUNCTION);                                 \<br>
+    sb_recorder->Record(static_cast<Result (*)()>(&Class::Method));            \<br>
+  }<br>
+<br>
+#define LLDB_RECORD_RESULT(Result)                                             \<br>
+  sb_recorder ? sb_recorder->RecordResult(Result) : Result;<br>
<br>
 namespace lldb_private {<br>
 namespace repro {<br>
@@ -139,9 +235,6 @@ public:<br>
     assert(result == 0);<br>
   }<br>
<br>
-protected:<br>
-  IndexToObject &GetIndexToObject() { return m_index_to_object; }<br>
-<br>
 private:<br>
   template <typename T> T Read(ValueTag) {<br>
     assert(HasData(sizeof(T)));<br>
@@ -222,6 +315,114 @@ template <> struct DeserializationHelper<br>
   };<br>
 };<br>
<br>
+/// The replayer interface.<br>
+struct Replayer {<br>
+  virtual ~Replayer() {}<br>
+  virtual void operator()(Deserializer &deserializer) const = 0;<br>
+};<br>
+<br>
+/// The default replayer deserializes the arguments and calls the function.<br>
+template <typename Signature> struct DefaultReplayer;<br>
+template <typename Result, typename... Args><br>
+struct DefaultReplayer<Result(Args...)> : public Replayer {<br>
+  DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {}<br>
+<br>
+  void operator()(Deserializer &deserializer) const override {<br>
+    deserializer.HandleReplayResult(<br>
+        DeserializationHelper<Args...>::template deserialized<Result>::doit(<br>
+            deserializer, f));<br>
+  }<br>
+<br>
+  Result (*f)(Args...);<br>
+};<br>
+<br>
+/// Partial specialization for function returning a void type. It ignores the<br>
+/// (absent) return value.<br>
+template <typename... Args><br>
+struct DefaultReplayer<void(Args...)> : public Replayer {<br>
+  DefaultReplayer(void (*f)(Args...)) : Replayer(), f(f) {}<br>
+<br>
+  void operator()(Deserializer &deserializer) const override {<br>
+    DeserializationHelper<Args...>::template deserialized<void>::doit(<br>
+        deserializer, f);<br>
+    deserializer.HandleReplayResultVoid();<br>
+  }<br>
+<br>
+  void (*f)(Args...);<br>
+};<br>
+<br>
+/// The registry contains a unique mapping between functions and their ID. The<br>
+/// IDs can be serialized and deserialized to replay a function. Functions need<br>
+/// to be registered with the registry for this to work.<br>
+class Registry {<br>
+public:<br>
+  Registry() = default;<br>
+  virtual ~Registry() = default;<br>
+<br>
+  /// Register a default replayer for a function.<br>
+  template <typename Signature> void Register(Signature *f) {<br>
+    DoRegister(uintptr_t(f), llvm::make_unique<DefaultReplayer<Signature>>(f));<br>
+  }<br>
+<br>
+  /// Register a replayer that invokes a custom function with the same<br>
+  /// signature as the replayed function.<br>
+  template <typename Signature> void Register(Signature *f, Signature *g) {<br>
+    DoRegister(uintptr_t(f), llvm::make_unique<DefaultReplayer<Signature>>(g));<br>
+  }<br>
+<br>
+  /// Replay functions from a file.<br>
+  bool Replay(const FileSpec &file);<br>
+<br>
+  /// Replay functions from a buffer.<br>
+  bool Replay(llvm::StringRef buffer);<br>
+<br>
+  /// Returns the ID for a given function address.<br>
+  unsigned GetID(uintptr_t addr);<br>
+<br>
+protected:<br>
+  /// Register the given replayer for a function (and the ID mapping).<br>
+  void DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer);<br>
+<br>
+private:<br>
+  /// Mapping of function addresses to replayers and their ID.<br>
+  std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>><br>
+      m_replayers;<br>
+<br>
+  /// Mapping of IDs to replayer instances.<br>
+  std::map<unsigned, Replayer *> m_ids;<br>
+};<br>
+<br>
+/// To be used as the "Runtime ID" of a constructor. It also invokes the<br>
+/// constructor when called.<br>
+template <typename Signature> struct construct;<br>
+template <typename Class, typename... Args> struct construct<Class(Args...)> {<br>
+  static Class *doit(Args... args) { return new Class(args...); }<br>
+};<br>
+<br>
+/// To be used as the "Runtime ID" of a member function. It also invokes the<br>
+/// member function when called.<br>
+template <typename Signature> struct invoke;<br>
+template <typename Result, typename Class, typename... Args><br>
+struct invoke<Result (Class::*)(Args...)> {<br>
+  template <Result (Class::*m)(Args...)> struct method {<br>
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }<br>
+  };<br>
+};<br>
+<br>
+template <typename Result, typename Class, typename... Args><br>
+struct invoke<Result (Class::*)(Args...) const> {<br>
+  template <Result (Class::*m)(Args...) const> struct method_const {<br>
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }<br>
+  };<br>
+};<br>
+<br>
+template <typename Class, typename... Args><br>
+struct invoke<void (Class::*)(Args...)> {<br>
+  template <void (Class::*m)(Args...)> struct method {<br>
+    static void doit(Class *c, Args... args) { (c->*m)(args...); }<br>
+  };<br>
+};<br>
+<br>
 /// Maps an object to an index for serialization. Indices are unique and<br>
 /// incremented for every new object.<br>
 ///<br>
@@ -236,7 +437,6 @@ public:<br>
 private:<br>
   unsigned GetIndexForObjectImpl(void *object);<br>
<br>
-  std::mutex m_mutex;<br>
   llvm::DenseMap<void *, unsigned> m_mapping;<br>
 };<br>
<br>
@@ -296,6 +496,114 @@ private:<br>
   ObjectToIndex m_tracker;<br>
 };<br>
<br>
+class InstrumentationData {<br>
+public:<br>
+  InstrumentationData() : m_serializer(nullptr), m_registry(nullptr){};<br>
+  InstrumentationData(Serializer &serializer, Registry &registry)<br>
+      : m_serializer(&serializer), m_registry(&registry){};<br>
+<br>
+  Serializer &GetSerializer() { return *m_serializer; }<br>
+  Registry &GetRegistry() { return *m_registry; }<br>
+<br>
+  operator bool() { return m_serializer != nullptr && m_registry != nullptr; }<br>
+<br>
+private:<br>
+  Serializer *m_serializer;<br>
+  Registry *m_registry;<br>
+};<br>
+<br>
+/// RAII object that tracks the function invocations and their return value.<br>
+///<br>
+/// API calls are only captured when the API boundary is crossed. Once we're in<br>
+/// the API layer, and another API function is called, it doesn't need to be<br>
+/// recorded.<br>
+///<br>
+/// When a call is recored, its result is always recorded as well, even if the<br>
+/// function returns a void. For functions that return by value, RecordResult<br>
+/// should be used. Otherwise a sentinel value (0) will be serialized.<br>
+class Recorder {<br>
+public:<br>
+  Recorder(Serializer &serializer, Registry &registry,<br>
+           llvm::StringRef pretty_func = {});<br>
+  ~Recorder();<br>
+<br>
+  /// Records a single function call.<br>
+  template <typename Result, typename... FArgs, typename... RArgs><br>
+  void Record(Result (*f)(FArgs...), const RArgs &... args) {<br>
+    if (!ShouldCapture())<br>
+      return;<br>
+<br>
+    unsigned id = m_registry.GetID(uintptr_t(f));<br>
+<br>
+    LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "#{0} '{1}'", id,<br>
+             m_pretty_func);<br>
+<br>
+    m_serializer.SerializeAll(id);<br>
+    m_serializer.SerializeAll(args...);<br>
+<br>
+    if (std::is_class<typename std::remove_pointer<<br>
+            typename std::remove_reference<Result>::type>::type>::value) {<br>
+      m_result_recorded = false;<br>
+    } else {<br>
+      m_serializer.SerializeAll(0);<br>
+      m_result_recorded = true;<br>
+    }<br>
+  }<br>
+<br>
+  /// Records a single function call.<br>
+  template <typename... Args><br>
+  void Record(void (*f)(Args...), const Args &... args) {<br>
+    if (!ShouldCapture())<br>
+      return;<br>
+<br>
+    unsigned id = m_registry.GetID(uintptr_t(f));<br>
+<br>
+    LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "#{0} '{1}'", id,<br>
+             m_pretty_func);<br>
+<br>
+    m_serializer.SerializeAll(id);<br>
+    m_serializer.SerializeAll(args...);<br>
+<br>
+    // Record result.<br>
+    m_serializer.SerializeAll(0);<br>
+    m_result_recorded = true;<br>
+  }<br>
+<br>
+  /// Record the result of a function call.<br>
+  template <typename Result> Result RecordResult(const Result &r) {<br>
+    UpdateBoundary();<br>
+    if (ShouldCapture()) {<br>
+      assert(!m_result_recorded);<br>
+      m_serializer.SerializeAll(r);<br>
+      m_result_recorded = true;<br>
+    }<br>
+    return r;<br>
+  }<br>
+<br>
+private:<br>
+  void UpdateBoundary() {<br>
+    if (m_local_boundary)<br>
+      g_global_boundary = false;<br>
+  }<br>
+<br>
+  bool ShouldCapture() { return m_local_boundary; }<br>
+<br>
+  Serializer &m_serializer;<br>
+  Registry &m_registry;<br>
+<br>
+  /// Pretty function for logging.<br>
+  llvm::StringRef m_pretty_func;<br>
+<br>
+  /// Whether this function call was the one crossing the API boundary.<br>
+  bool m_local_boundary;<br>
+<br>
+  /// Whether the return value was recorded explicitly.<br>
+  bool m_result_recorded;<br>
+<br>
+  /// Whether we're currently across the API boundary.<br>
+  static bool g_global_boundary;<br>
+};<br>
+<br>
 } // namespace repro<br>
 } // namespace lldb_private<br>
<br>
<br>
Modified: lldb/trunk/source/API/CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/CMakeLists.txt?rev=353324&r1=353323&r2=353324&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/CMakeLists.txt?rev=353324&r1=353323&r2=353324&view=diff</a><br>
==============================================================================<br>
--- lldb/trunk/source/API/CMakeLists.txt (original)<br>
+++ lldb/trunk/source/API/CMakeLists.txt Wed Feb  6 10:57:42 2019<br>
@@ -50,6 +50,7 @@ add_lldb_library(liblldb SHARED<br>
   SBProcessInfo.cpp<br>
   SBQueue.cpp<br>
   SBQueueItem.cpp<br>
+  SBReproducer.cpp<br>
   SBSection.cpp<br>
   SBSourceManager.cpp<br>
   SBStream.cpp<br>
<br>
Added: lldb/trunk/source/API/SBReproducer.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBReproducer.cpp?rev=353324&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBReproducer.cpp?rev=353324&view=auto</a><br>
==============================================================================<br>
--- lldb/trunk/source/API/SBReproducer.cpp (added)<br>
+++ lldb/trunk/source/API/SBReproducer.cpp Wed Feb  6 10:57:42 2019<br>
@@ -0,0 +1,51 @@<br>
+//===-- SBReproducer.cpp ----------------------------------------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "SBReproducerPrivate.h"<br>
+<br>
+#include "lldb/API/LLDB.h"<br>
+#include "lldb/API/SBAddress.h"<br>
+#include "lldb/API/SBAttachInfo.h"<br>
+#include "lldb/API/SBBlock.h"<br>
+#include "lldb/API/SBBreakpoint.h"<br>
+#include "lldb/API/SBCommandInterpreter.h"<br>
+#include "lldb/API/SBData.h"<br>
+#include "lldb/API/SBDebugger.h"<br>
+#include "lldb/API/SBDeclaration.h"<br>
+#include "lldb/API/SBError.h"<br>
+#include "lldb/API/SBFileSpec.h"<br>
+#include "lldb/API/SBHostOS.h"<br>
+#include "lldb/API/SBReproducer.h"<br>
+<br>
+#include "lldb/Host/FileSystem.h"<br>
+<br>
+using namespace lldb;<br>
+using namespace lldb_private;<br>
+using namespace lldb_private::repro;<br>
+<br>
+SBRegistry::SBRegistry() {}<br>
+<br>
+bool SBReproducer::Replay() {<br>
+  repro::Loader *loader = repro::Reproducer::Instance().GetLoader();<br>
+  if (!loader)<br>
+    return false;<br>
+<br>
+  FileSpec file = loader->GetFile<SBInfo>();<br>
+  if (!file)<br>
+    return false;<br>
+<br>
+  SBRegistry registry;<br>
+  registry.Replay(file);<br>
+<br>
+  return true;<br>
+}<br>
+<br>
+char lldb_private::repro::SBProvider::ID = 0;<br>
+const char *SBInfo::name = "sbapi";<br>
+const char *SBInfo::file = "sbapi.bin";<br>
<br>
Added: lldb/trunk/source/API/SBReproducerPrivate.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBReproducerPrivate.h?rev=353324&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/source/API/SBReproducerPrivate.h?rev=353324&view=auto</a><br>
==============================================================================<br>
--- lldb/trunk/source/API/SBReproducerPrivate.h (added)<br>
+++ lldb/trunk/source/API/SBReproducerPrivate.h Wed Feb  6 10:57:42 2019<br>
@@ -0,0 +1,72 @@<br>
+//===-- SBReproducerPrivate.h -----------------------------------*- C++ -*-===//<br>
+//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLDB_API_SBREPRODUCER_PRIVATE_H<br>
+#define LLDB_API_SBREPRODUCER_PRIVATE_H<br>
+<br>
+#include "lldb/API/SBReproducer.h"<br>
+<br>
+#include "lldb/Utility/FileSpec.h"<br>
+#include "lldb/Utility/Log.h"<br>
+#include "lldb/Utility/Reproducer.h"<br>
+#include "lldb/Utility/ReproducerInstrumentation.h"<br>
+<br>
+#include "llvm/ADT/DenseMap.h"<br>
+<br>
+#define LLDB_GET_INSTRUMENTATION_DATA()                                        \<br>
+  lldb_private::repro::GetInstrumentationData()<br>
+<br>
+namespace lldb_private {<br>
+namespace repro {<br>
+<br>
+class SBRegistry : public Registry {<br>
+public:<br>
+  SBRegistry();<br>
+};<br>
+<br>
+struct SBInfo {<br>
+  static const char *name;<br>
+  static const char *file;<br>
+};<br>
+<br>
+class SBProvider : public Provider<SBProvider> {<br>
+public:<br>
+  typedef SBInfo info;<br>
+<br>
+  SBProvider(const FileSpec &directory)<br>
+      : Provider(directory),<br>
+        m_stream(directory.CopyByAppendingPathComponent("sbapi.bin").GetPath(),<br>
+                 m_ec, llvm::sys::fs::OpenFlags::F_None),<br>
+        m_serializer(m_stream) {}<br>
+<br>
+  Serializer &GetSerializer() { return m_serializer; }<br>
+  Registry &GetRegistry() { return m_registry; }<br>
+<br>
+  static char ID;<br>
+<br>
+private:<br>
+  std::error_code m_ec;<br>
+  llvm::raw_fd_ostream m_stream;<br>
+  Serializer m_serializer;<br>
+  SBRegistry m_registry;<br>
+};<br>
+<br>
+inline InstrumentationData GetInstrumentationData() {<br>
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {<br>
+    auto &p = g->GetOrCreate<SBProvider>();<br>
+    return {p.GetSerializer(), p.GetRegistry()};<br>
+  }<br>
+  return {};<br>
+}<br>
+<br>
+} // namespace repro<br>
+} // namespace lldb_private<br>
+<br>
+#endif<br>
<br>
Modified: lldb/trunk/source/Utility/ReproducerInstrumentation.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/ReproducerInstrumentation.cpp?rev=353324&r1=353323&r2=353324&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Utility/ReproducerInstrumentation.cpp?rev=353324&r1=353323&r2=353324&view=diff</a><br>
==============================================================================<br>
--- lldb/trunk/source/Utility/ReproducerInstrumentation.cpp (original)<br>
+++ lldb/trunk/source/Utility/ReproducerInstrumentation.cpp Wed Feb  6 10:57:42 2019<br>
@@ -34,11 +34,62 @@ template <> const char *Deserializer::De<br>
   return str;<br>
 }<br>
<br>
+bool Registry::Replay(const FileSpec &file) {<br>
+  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());<br>
+  if (auto err = error_or_file.getError())<br>
+    return false;<br>
+<br>
+  return Replay((*error_or_file)->getBuffer());<br>
+}<br>
+<br>
+bool Registry::Replay(llvm::StringRef buffer) {<br>
+  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);<br>
+<br>
+  Deserializer deserializer(buffer);<br>
+  while (deserializer.HasData(1)) {<br>
+    unsigned id = deserializer.Deserialize<unsigned>();<br>
+    LLDB_LOG(log, "Replaying function #{0}", id);<br>
+    m_ids[id]->operator()(deserializer);<br>
+  }<br>
+<br>
+  return true;<br>
+}<br>
+<br>
+void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer) {<br>
+  const unsigned id = m_replayers.size() + 1;<br>
+  assert(m_replayers.find(RunID) == m_replayers.end());<br>
+  m_replayers[RunID] = std::make_pair(std::move(replayer), id);<br>
+  m_ids[id] = m_replayers[RunID].first.get();<br>
+}<br>
+<br>
+unsigned Registry::GetID(uintptr_t addr) {<br>
+  unsigned id = m_replayers[addr].second;<br>
+  assert(id != 0 && "Forgot to add function to registry?");<br>
+  return id;<br>
+}<br>
+<br>
 unsigned ObjectToIndex::GetIndexForObjectImpl(void *object) {<br>
-  std::lock_guard<std::mutex> guard(m_mutex);<br>
   unsigned index = m_mapping.size() + 1;<br>
   auto it = m_mapping.find(object);<br>
   if (it == m_mapping.end())<br>
     m_mapping[object] = index;<br>
   return m_mapping[object];<br>
 }<br>
+<br>
+Recorder::Recorder(Serializer &serializer, Registry &registry,<br>
+                   llvm::StringRef pretty_func)<br>
+    : m_serializer(serializer), m_registry(registry),<br>
+      m_pretty_func(pretty_func), m_local_boundary(false),<br>
+      m_result_recorded(true) {<br>
+  if (!g_global_boundary) {<br>
+    g_global_boundary = true;<br>
+    m_local_boundary = true;<br>
+  }<br>
+}<br>
+<br>
+Recorder::~Recorder() {<br>
+  assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?");<br>
+  UpdateBoundary();<br>
+}<br>
+<br>
+bool lldb_private::repro::Recorder::g_global_boundary;<br>
<br>
Modified: lldb/trunk/tools/driver/Driver.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.cpp?rev=353324&r1=353323&r2=353324&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/driver/Driver.cpp?rev=353324&r1=353323&r2=353324&view=diff</a><br>
==============================================================================<br>
--- lldb/trunk/tools/driver/Driver.cpp (original)<br>
+++ lldb/trunk/tools/driver/Driver.cpp Wed Feb  6 10:57:42 2019<br>
@@ -13,6 +13,7 @@<br>
 #include "lldb/API/SBDebugger.h"<br>
 #include "lldb/API/SBHostOS.h"<br>
 #include "lldb/API/SBLanguageRuntime.h"<br>
+#include "lldb/API/SBReproducer.h"<br>
 #include "lldb/API/SBStream.h"<br>
 #include "lldb/API/SBStringList.h"<br>
<br>
@@ -888,8 +889,10 @@ main(int argc, char const *argv[])<br>
                          << '\n';<br>
   }<br>
<br>
-  SBInitializerOptions options;<br>
+  // Remember if we're in replay mode for later.<br>
+  bool replay = false;<br>
<br>
+  SBInitializerOptions options;<br>
   if (auto *arg = input_args.getLastArg(OPT_capture)) {<br>
     auto arg_value = arg->getValue();<br>
     options.SetReproducerPath(arg_value);<br>
@@ -900,6 +903,7 @@ main(int argc, char const *argv[])<br>
     auto arg_value = arg->getValue();<br>
     options.SetReplayReproducer(true);<br>
     options.SetReproducerPath(arg_value);<br>
+    replay = true;<br>
   }<br>
<br>
   SBError error = SBDebugger::Initialize(options);<br>
@@ -909,6 +913,14 @@ main(int argc, char const *argv[])<br>
     return 1;<br>
   }<br>
<br>
+  if (replay) {<br>
+    SBReproducer reproducer;<br>
+    if (!reproducer.Replay()) {<br>
+      WithColor::error() << "something went wrong running the reporducer.\n";<br>
+    }<br>
+    return 0;<br>
+  }<br>
+<br>
   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");<br>
<br>
   signal(SIGINT, sigint_handler);<br>
<br>
Modified: lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp?rev=353324&r1=353323&r2=353324&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp?rev=353324&r1=353323&r2=353324&view=diff</a><br>
==============================================================================<br>
--- lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp (original)<br>
+++ lldb/trunk/unittests/Utility/ReproducerInstrumentationTest.cpp Wed Feb  6 10:57:42 2019<br>
@@ -9,12 +9,14 @@<br>
 #include "gmock/gmock.h"<br>
 #include "gtest/gtest.h"<br>
<br>
+#include <cmath><br>
+#include <limits><br>
+<br>
 #include "lldb/Utility/ReproducerInstrumentation.h"<br>
<br>
 using namespace lldb_private;<br>
 using namespace lldb_private::repro;<br>
<br>
-namespace {<br>
 struct Foo {<br>
   int m = 1;<br>
 };<br>
@@ -40,7 +42,205 @@ struct Pod {<br>
   unsigned long l = 8;<br>
   unsigned short m = 9;<br>
 };<br>
-} // namespace<br>
+<br>
+class TestingRegistry : public Registry {<br>
+public:<br>
+  TestingRegistry();<br>
+};<br>
+<br>
+static llvm::Optional<Serializer> g_serializer;<br>
+static llvm::Optional<TestingRegistry> g_registry;<br>
+<br>
+#define LLDB_GET_INSTRUMENTATION_DATA()                                        \<br>
+  InstrumentationData(*g_serializer, *g_registry)<br>
+<br>
+class InstrumentedFoo {<br>
+public:<br>
+  InstrumentedFoo() = default;<br>
+  /// Instrumented methods.<br>
+  /// {<br>
+  InstrumentedFoo(int i);<br>
+  InstrumentedFoo(const InstrumentedFoo &foo);<br>
+  InstrumentedFoo &operator=(const InstrumentedFoo &foo);<br>
+  void A(int a);<br>
+  void B(int &b) const;<br>
+  int C(float *c);<br>
+  int D(const char *d) const;<br>
+  static void E(double e);<br>
+  static int F();<br>
+  void Validate();<br>
+  //// }<br>
+<br>
+private:<br>
+  int m_a = 0;<br>
+  mutable int m_b = 0;<br>
+  float m_c = 0;<br>
+  mutable std::string m_d = {};<br>
+  static double g_e;<br>
+  static bool g_f;<br>
+  mutable int m_called = 0;<br>
+};<br>
+<br>
+class InstrumentedBar {<br>
+public:<br>
+  /// Instrumented methods.<br>
+  /// {<br>
+  InstrumentedBar();<br>
+  InstrumentedFoo GetInstrumentedFoo();<br>
+  void SetInstrumentedFoo(InstrumentedFoo *foo);<br>
+  void SetInstrumentedFoo(InstrumentedFoo &foo);<br>
+  void Validate();<br>
+  /// }<br>
+<br>
+private:<br>
+  bool m_get_instrumend_foo_called = false;<br>
+  InstrumentedFoo *m_foo_set_by_ptr = nullptr;<br>
+  InstrumentedFoo *m_foo_set_by_ref = nullptr;<br>
+};<br>
+<br>
+double InstrumentedFoo::g_e = 0;<br>
+bool InstrumentedFoo::g_f = false;<br>
+<br>
+static std::vector<InstrumentedFoo *> g_foos;<br>
+static std::vector<InstrumentedBar *> g_bars;<br>
+<br>
+void ClearObjects() {<br>
+  g_foos.clear();<br>
+  g_bars.clear();<br>
+}<br>
+<br>
+void ValidateObjects(size_t expected_foos, size_t expected_bars) {<br>
+  EXPECT_EQ(expected_foos, g_foos.size());<br>
+  EXPECT_EQ(expected_bars, g_bars.size());<br>
+<br>
+  for (auto *foo : g_foos) {<br>
+    foo->Validate();<br>
+  }<br>
+<br>
+  for (auto *bar : g_bars) {<br>
+    bar->Validate();<br>
+  }<br>
+}<br>
+<br>
+InstrumentedFoo::InstrumentedFoo(int i) {<br>
+  LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (int), i);<br>
+  g_foos.push_back(this);<br>
+}<br>
+<br>
+InstrumentedFoo::InstrumentedFoo(const InstrumentedFoo &foo) {<br>
+  LLDB_RECORD_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &), foo);<br>
+  g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo));<br>
+  g_foos.push_back(this);<br>
+}<br>
+<br>
+InstrumentedFoo &InstrumentedFoo::operator=(const InstrumentedFoo &foo) {<br>
+  LLDB_RECORD_METHOD(InstrumentedFoo &,<br>
+                     InstrumentedFoo, operator=,(const InstrumentedFoo &), foo);<br>
+  g_foos.erase(std::remove(g_foos.begin(), g_foos.end(), &foo));<br>
+  g_foos.push_back(this);<br>
+  return *this;<br>
+}<br>
+<br>
+void InstrumentedFoo::A(int a) {<br>
+  LLDB_RECORD_METHOD(void, InstrumentedFoo, A, (int), a);<br>
+  B(a);<br>
+  m_a = a;<br>
+}<br>
+<br>
+void InstrumentedFoo::B(int &b) const {<br>
+  LLDB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b);<br>
+  m_called++;<br>
+  m_b = b;<br>
+}<br>
+<br>
+int InstrumentedFoo::C(float *c) {<br>
+  LLDB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c);<br>
+  m_c = *c;<br>
+  return 1;<br>
+}<br>
+<br>
+int InstrumentedFoo::D(const char *d) const {<br>
+  LLDB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d);<br>
+  m_d = std::string(d);<br>
+  return 2;<br>
+}<br>
+<br>
+void InstrumentedFoo::E(double e) {<br>
+  LLDB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e);<br>
+  g_e = e;<br>
+}<br>
+<br>
+int InstrumentedFoo::F() {<br>
+  LLDB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F);<br>
+  g_f = true;<br>
+  return 3;<br>
+}<br>
+<br>
+void InstrumentedFoo::Validate() {<br>
+  LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedFoo, Validate);<br>
+  EXPECT_EQ(m_a, 100);<br>
+  EXPECT_EQ(m_b, 200);<br>
+  EXPECT_NEAR(m_c, 300.3, 0.01);<br>
+  EXPECT_EQ(m_d, "bar");<br>
+  EXPECT_NEAR(g_e, 400.4, 0.01);<br>
+  EXPECT_EQ(g_f, true);<br>
+  EXPECT_EQ(2, m_called);<br>
+}<br>
+<br>
+InstrumentedBar::InstrumentedBar() {<br>
+  LLDB_RECORD_CONSTRUCTOR_NO_ARGS(InstrumentedBar);<br>
+  g_bars.push_back(this);<br>
+}<br>
+<br>
+InstrumentedFoo InstrumentedBar::GetInstrumentedFoo() {<br>
+  LLDB_RECORD_METHOD_NO_ARGS(InstrumentedFoo, InstrumentedBar,<br>
+                             GetInstrumentedFoo);<br>
+  m_get_instrumend_foo_called = true;<br>
+  return LLDB_RECORD_RESULT(InstrumentedFoo(0));<br>
+}<br>
+<br>
+void InstrumentedBar::SetInstrumentedFoo(InstrumentedFoo *foo) {<br>
+  LLDB_RECORD_METHOD(void, InstrumentedBar, SetInstrumentedFoo,<br>
+                     (InstrumentedFoo *), foo);<br>
+  m_foo_set_by_ptr = foo;<br>
+}<br>
+<br>
+void InstrumentedBar::SetInstrumentedFoo(InstrumentedFoo &foo) {<br>
+  LLDB_RECORD_METHOD(void, InstrumentedBar, SetInstrumentedFoo,<br>
+                     (InstrumentedFoo &), foo);<br>
+  m_foo_set_by_ref = &foo;<br>
+}<br>
+<br>
+void InstrumentedBar::Validate() {<br>
+  LLDB_RECORD_METHOD_NO_ARGS(void, InstrumentedBar, Validate);<br>
+<br>
+  EXPECT_TRUE(m_get_instrumend_foo_called);<br>
+  EXPECT_NE(m_foo_set_by_ptr, nullptr);<br>
+  EXPECT_EQ(m_foo_set_by_ptr, m_foo_set_by_ref);<br>
+}<br>
+<br>
+TestingRegistry::TestingRegistry() {<br>
+  LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (int i));<br>
+  LLDB_REGISTER_CONSTRUCTOR(InstrumentedFoo, (const InstrumentedFoo &));<br>
+  LLDB_REGISTER_METHOD(InstrumentedFoo &,<br>
+                       InstrumentedFoo, operator=,(const InstrumentedFoo &));<br>
+  LLDB_REGISTER_METHOD(void, InstrumentedFoo, A, (int));<br>
+  LLDB_REGISTER_METHOD_CONST(void, InstrumentedFoo, B, (int &));<br>
+  LLDB_REGISTER_METHOD(int, InstrumentedFoo, C, (float *));<br>
+  LLDB_REGISTER_METHOD_CONST(int, InstrumentedFoo, D, (const char *));<br>
+  LLDB_REGISTER_STATIC_METHOD(void, InstrumentedFoo, E, (double));<br>
+  LLDB_REGISTER_STATIC_METHOD(int, InstrumentedFoo, F, ());<br>
+  LLDB_REGISTER_METHOD(void, InstrumentedFoo, Validate, ());<br>
+<br>
+  LLDB_REGISTER_CONSTRUCTOR(InstrumentedBar, ());<br>
+  LLDB_REGISTER_METHOD(InstrumentedFoo, InstrumentedBar, GetInstrumentedFoo,<br>
+                       ());<br>
+  LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo,<br>
+                       (InstrumentedFoo *));<br>
+  LLDB_REGISTER_METHOD(void, InstrumentedBar, SetInstrumentedFoo,<br>
+                       (InstrumentedFoo &));<br>
+  LLDB_REGISTER_METHOD(void, InstrumentedBar, Validate, ());<br>
+}<br>
<br>
 static const Pod p;<br>
<br>
@@ -206,3 +406,105 @@ TEST(SerializationRountripTest, Serializ<br>
   EXPECT_EQ(foo, deserializer.Deserialize<Foo &>());<br>
   EXPECT_EQ(bar, deserializer.Deserialize<Bar &>());<br>
 }<br>
+<br>
+TEST(RecordReplayTest, InstrumentedFoo) {<br>
+  std::string str;<br>
+  llvm::raw_string_ostream os(str);<br>
+  g_registry.emplace();<br>
+  g_serializer.emplace(os);<br>
+<br>
+  {<br>
+    int b = 200;<br>
+    float c = 300.3;<br>
+    double e = 400.4;<br>
+<br>
+    InstrumentedFoo foo(0);<br>
+    foo.A(100);<br>
+    foo.B(b);<br>
+    foo.C(&c);<br>
+    foo.D("bar");<br>
+    InstrumentedFoo::E(e);<br>
+    InstrumentedFoo::F();<br>
+    foo.Validate();<br>
+  }<br>
+<br>
+  ClearObjects();<br>
+<br>
+  TestingRegistry registry;<br>
+  registry.Replay(os.str());<br>
+<br>
+  ValidateObjects(1, 0);<br>
+}<br>
+<br>
+TEST(RecordReplayTest, InstrumentedFooSameThis) {<br>
+  std::string str;<br>
+  llvm::raw_string_ostream os(str);<br>
+  g_registry.emplace();<br>
+  g_serializer.emplace(os);<br>
+<br>
+  int b = 200;<br>
+  float c = 300.3;<br>
+  double e = 400.4;<br>
+<br>
+  InstrumentedFoo *foo = new InstrumentedFoo(0);<br>
+  foo->A(100);<br>
+  foo->B(b);<br>
+  foo->C(&c);<br>
+  foo->D("bar");<br>
+  InstrumentedFoo::E(e);<br>
+  InstrumentedFoo::F();<br>
+  foo->Validate();<br>
+  foo->~InstrumentedFoo();<br>
+<br>
+  InstrumentedFoo *foo2 = new (foo) InstrumentedFoo(0);<br>
+  foo2->A(100);<br>
+  foo2->B(b);<br>
+  foo2->C(&c);<br>
+  foo2->D("bar");<br>
+  InstrumentedFoo::E(e);<br>
+  InstrumentedFoo::F();<br>
+  foo2->Validate();<br>
+  delete foo2;<br>
+<br>
+  ClearObjects();<br>
+<br>
+  TestingRegistry registry;<br>
+  registry.Replay(os.str());<br>
+<br>
+  ValidateObjects(2, 0);<br>
+}<br>
+<br>
+TEST(RecordReplayTest, InstrumentedBar) {<br>
+  std::string str;<br>
+  llvm::raw_string_ostream os(str);<br>
+  g_registry.emplace();<br>
+  g_serializer.emplace(os);<br>
+<br>
+  {<br>
+    InstrumentedBar bar;<br>
+    InstrumentedFoo foo = bar.GetInstrumentedFoo();<br>
+<br>
+    int b = 200;<br>
+    float c = 300.3;<br>
+    double e = 400.4;<br>
+<br>
+    foo.A(100);<br>
+    foo.B(b);<br>
+    foo.C(&c);<br>
+    foo.D("bar");<br>
+    InstrumentedFoo::E(e);<br>
+    InstrumentedFoo::F();<br>
+    foo.Validate();<br>
+<br>
+    bar.SetInstrumentedFoo(foo);<br>
+    bar.SetInstrumentedFoo(&foo);<br>
+    bar.Validate();<br>
+  }<br>
+<br>
+  ClearObjects();<br>
+<br>
+  TestingRegistry registry;<br>
+  registry.Replay(os.str());<br>
+<br>
+  ValidateObjects(1, 1);<br>
+}<br>
<br>
<br>
_______________________________________________<br>
lldb-commits mailing list<br>
<a href="mailto:lldb-commits@lists.llvm.org" target="_blank">lldb-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits</a><br>
</blockquote></div>