JDevlieghere updated this revision to Diff 181868.
JDevlieghere added a comment.
Herald added a subscriber: lldb-commits.

Prototype 2.0


Repository:
  rLLDB LLDB

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56322/new/

https://reviews.llvm.org/D56322

Files:
  include/lldb/API/SBReproducer.h
  include/lldb/Utility/Reproducer.h
  source/API/CMakeLists.txt
  source/API/SBCommandInterpreter.cpp
  source/API/SBCommandReturnObject.cpp
  source/API/SBDebugger.cpp
  source/API/SBFileSpec.cpp
  source/API/SBHostOS.cpp
  source/API/SBReproducer.cpp
  source/API/SBReproducerPrivate.h
  tools/driver/Driver.cpp

Index: tools/driver/Driver.cpp
===================================================================
--- tools/driver/Driver.cpp
+++ tools/driver/Driver.cpp
@@ -14,6 +14,7 @@
 #include "lldb/API/SBDebugger.h"
 #include "lldb/API/SBHostOS.h"
 #include "lldb/API/SBLanguageRuntime.h"
+#include "lldb/API/SBReproducer.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 
@@ -910,6 +911,11 @@
     return 1;
   }
 
+  SBReproducer reproducer;
+  if (reproducer.Replay()) {
+    return 0;
+  }
+
   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
 
   signal(SIGINT, sigint_handler);
Index: source/API/SBReproducerPrivate.h
===================================================================
--- /dev/null
+++ source/API/SBReproducerPrivate.h
@@ -0,0 +1,730 @@
+//===-- SBReproducerPrivate.h -----------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SBREPRODUCER_PRIVATE_H
+#define LLDB_API_SBREPRODUCER_PRIVATE_H
+
+#include "lldb/API/SBReproducer.h"
+
+#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/Reproducer.h"
+
+#include "llvm/ADT/DenseMap.h"
+
+#include <mutex>
+
+namespace lldb_private {
+namespace repro {
+
+/// Mapping between serialized indices and their corresponding objects.
+///
+/// This class is used during replay to map indices back to in-memory objects.
+///
+/// When objects are constructed, they are added to this mapping using
+/// AddObjectForIndex.
+///
+/// When an object is passed to a function, its index is deserialized and
+/// AddObjectForIndex returns the corresponding object. If there is no object
+/// for the given index, a nullptr is returend. The latter is valid when custom
+/// replay code is in place and the actual object is ignored.
+class SBIndexToObject {
+public:
+  /// Returns an object as a pointer for the given index or nullptr if not
+  /// present in the map.
+  template <typename T> T *GetObjectForIndex(int idx) {
+    assert(idx != 0 && "Cannot get object for sentinel");
+    void *object = GetObjectForIndexImpl(idx);
+    return static_cast<typename std::remove_const<T>::type *>(object);
+  }
+
+  /// Adds a pointer to an object to the mapping for the given index.
+  template <typename T> void AddObjectForIndex(int idx, T *object) {
+    AddObjectForIndexImpl(
+        idx, static_cast<void *>(
+                 const_cast<typename std::remove_const<T>::type *>(object)));
+  }
+
+  /// Adds a reference to an object to the mapping for the given index.
+  template <typename T> void AddObjectForIndex(int idx, T &object) {
+    AddObjectForIndexImpl(
+        idx, static_cast<void *>(
+                 const_cast<typename std::remove_const<T>::type *>(&object)));
+  }
+
+private:
+  /// Helper method that does the actual lookup. The void* result is later cast
+  /// by the caller.
+  void *GetObjectForIndexImpl(int idx) {
+    auto it = m_mapping.find(idx);
+    if (it == m_mapping.end()) {
+      return nullptr;
+    }
+    return m_mapping[idx];
+  }
+
+  /// Helper method that does the actual insertion.
+  void AddObjectForIndexImpl(int idx, void *object) {
+    assert(idx != 0 && "Cannot add object for sentinel");
+    m_mapping[idx] = object;
+  }
+
+  /// Keeps a mapping between indices and their corresponding object.
+  llvm::DenseMap<unsigned, void *> m_mapping;
+};
+
+/// Base class for tag dispatch used in the SBDeserializer. Different tags are
+/// instantiated with different values.
+template <unsigned> struct SBTag {};
+
+/// We need to differentiate between pointers to fundamental and
+/// non-fundamental types. See the corresponding SBDeserializer::Read method
+/// for the reason why.
+typedef SBTag<0> PointerTag;
+typedef SBTag<1> ReferenceTag;
+typedef SBTag<2> ValueTag;
+typedef SBTag<3> FundamentalPointerTag;
+typedef SBTag<4> FundamentalReferenceTag;
+
+/// Return the deserialization tag for the given type T.
+template <class T> struct serializer_tag { typedef ValueTag type; };
+template <class T> struct serializer_tag<T *> {
+  typedef
+      typename std::conditional<std::is_fundamental<T>::value,
+                                FundamentalPointerTag, PointerTag>::type type;
+};
+template <class T> struct serializer_tag<T &> {
+  typedef typename std::conditional<std::is_fundamental<T>::value,
+                                    FundamentalReferenceTag, ReferenceTag>::type
+      type;
+};
+
+/// Deserializes data from a buffer. It is used to deserialize function indices
+/// to replay, their arguments and return values.
+///
+/// Fundamental types and strings are read by value. Objects are read by their
+/// index, which get translated by the SBIndexToObject mapping maintained in
+/// this class.
+///
+/// Additional bookkeeping with regards to the SBIndexToObject 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 SBDeserializer {
+public:
+  SBDeserializer(llvm::StringRef buffer = {}) : m_buffer(buffer), m_offset(0) {}
+
+  /// Returns true when the buffer has unread data.
+  bool HasData(int offset = 0) { return m_offset + offset < m_buffer.size(); }
+
+  /// Deserialize and interpret value as T.
+  template <typename T> T Deserialize() {
+    return Read<T>(typename serializer_tag<T>::type());
+  }
+
+  /// Store the returned value in the index-to-object mapping.
+  template <typename T> void HandleReplayResult(const T &t) {
+    unsigned result = Deserialize<unsigned>();
+    if (std::is_fundamental<T>::value)
+      return;
+    // We need to make a copy as the original object might go out of scope.
+    m_index_to_object.AddObjectForIndex(result, new T(t));
+  }
+
+  /// Store the returned value in the index-to-object mapping.
+  template <typename T> void HandleReplayResult(T *t) {
+    unsigned result = Deserialize<unsigned>();
+    if (std::is_fundamental<T>::value)
+      return;
+    m_index_to_object.AddObjectForIndex(result, t);
+  }
+
+  /// All returned types are recorded, even when the function returns a void.
+  /// The latter requires special handling.
+  void HandleReplayResultVoid() {
+    unsigned result = Deserialize<unsigned>();
+    assert(result == 0);
+  }
+
+  // FIXME: We have references to this instance stored in replayer instance. We
+  // should find a better way to swap out the buffer after this instance has
+  // been created, but his will have to do for now.
+  void LoadBuffer(llvm::StringRef buffer) {
+    m_buffer = buffer;
+    m_offset = 0;
+  }
+
+private:
+  template <typename T> T Read(ValueTag) {
+    T t;
+    std::memcpy((char *)&t, &m_buffer.data()[m_offset], sizeof(t));
+    m_offset += sizeof(t);
+    return t;
+  }
+
+  template <typename T> T Read(PointerTag) {
+    typedef typename std::remove_pointer<T>::type UnderlyingT;
+    return m_index_to_object.template GetObjectForIndex<UnderlyingT>(
+        Deserialize<unsigned>());
+  }
+
+  template <typename T> T Read(ReferenceTag) {
+    typedef typename std::remove_reference<T>::type UnderlyingT;
+    // If this is a reference to a fundamental type we just read its value.
+    return *m_index_to_object.template GetObjectForIndex<UnderlyingT>(
+        Deserialize<unsigned>());
+  }
+
+  /// This method is used to parse references to fundamental types. Because
+  /// they're not recorded in the object table we have serialized their value.
+  /// We read its value, allocate a copy on the heap, and return a pointer to
+  /// the copy.
+  template <typename T> T Read(FundamentalPointerTag) {
+    typedef typename std::remove_pointer<T>::type UnderlyingT;
+    return new UnderlyingT(Deserialize<UnderlyingT>());
+  }
+
+  /// This method is used to parse references to fundamental types. Because
+  /// they're not recorded in the object table we have serialized their value.
+  /// We read its value, allocate a copy on the heap, and return a reference to
+  /// the copy.
+  template <typename T> T Read(FundamentalReferenceTag) {
+    // If this is a reference to a fundamental type we just read its value.
+    typedef typename std::remove_reference<T>::type UnderlyingT;
+    return *(new UnderlyingT(Deserialize<UnderlyingT>()));
+  }
+
+  /// Mapping of indices to objects.
+  SBIndexToObject m_index_to_object;
+
+  /// Buffer containing the serialized data.
+  llvm::StringRef m_buffer;
+
+  /// Current offset in the buffer.
+  uint32_t m_offset;
+};
+
+/// Partial specialization for C-style strings. We read the string value
+/// instead of treating it as pointer.
+template <> const char *SBDeserializer::Deserialize<const char *>();
+
+/// Helpers to auto-synthesize function replay code. It deserializes the replay
+/// function's arguments one by one and finally calls the corresponding
+/// function.
+template <typename... Remaining> struct DeserializationHelper;
+
+template <typename Head, typename... Tail>
+struct DeserializationHelper<Head, Tail...> {
+  template <typename Result, typename... Deserialized> struct deserialized {
+    static Result doit(SBDeserializer &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(SBDeserializer &deserializer,
+                       Result (*f)(Deserialized...), Deserialized... d) {
+      return f(d...);
+    }
+  };
+};
+
+/// The replayer interface.
+struct SBReplayer {
+  SBReplayer(SBDeserializer &deserializer) : m_deserializer(deserializer) {}
+  virtual ~SBReplayer() {}
+  virtual void operator()() const = 0;
+
+protected:
+  SBDeserializer &m_deserializer;
+};
+
+/// 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 SBReplayer {
+  DefaultReplayer(SBDeserializer &deserializer, Result (*f)(Args...))
+      : SBReplayer(deserializer), f(f) {}
+
+  void operator()() const override {
+    m_deserializer.HandleReplayResult(
+        DeserializationHelper<Args...>::template deserialized<Result>::doit(
+            m_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 SBReplayer {
+  DefaultReplayer(SBDeserializer &deserializer, void (*f)(Args...))
+      : SBReplayer(deserializer), f(f) {}
+
+  void operator()() const override {
+    DeserializationHelper<Args...>::template deserialized<void>::doit(
+        m_deserializer, f);
+    m_deserializer.HandleReplayResultVoid();
+  }
+
+  void (*f)(Args...);
+};
+
+/// The custom replayer is similar to the default replayer but doesn't invoke
+/// the original function directly, but a different function with the same
+/// interface. The allows us to intercept replay calls and have custom
+/// implementation, for example to ignore a particular argument.
+template <typename Signature> struct CustomReplayer;
+template <typename Result, typename... Args>
+struct CustomReplayer<Result(Args...)> : public SBReplayer {
+  CustomReplayer(SBDeserializer &deserializer, Result (*f)(Args...),
+                 Result (*g)(Args...))
+      : SBReplayer(deserializer), f(f), g(g) {}
+
+  void operator()() const override {
+    m_deserializer.HandleReplayResult(
+        DeserializationHelper<Args...>::template deserialized<Result>::doit(
+            m_deserializer, f));
+  }
+
+  /// The replayed function.
+  Result (*f)(Args...);
+  /// A custom function.
+  Result (*g)(Args...);
+};
+
+/// Partial specialization for function returning a void type.
+template <typename... Args>
+struct CustomReplayer<void(Args...)> : public SBReplayer {
+  CustomReplayer(SBDeserializer &deserializer, void (*f)(Args...),
+                 void (*g)(Args...))
+      : SBReplayer(deserializer), f(f), g(g) {}
+
+  void operator()() const override {
+    DeserializationHelper<Args...>::template deserialized<void>::doit(
+        m_deserializer, f);
+    m_deserializer.HandleReplayResultVoid();
+  }
+
+  void (*f)(Args...);
+  void (*g)(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 SBRegistry {
+public:
+  static SBRegistry &Instance() {
+    static SBRegistry g_registry;
+    return g_registry;
+  }
+
+  SBRegistry() : m_id(1) { Init(); }
+
+  /// Register a default replayer for a function.
+  template <typename Signature> void Register(Signature *f, unsigned ID) {
+    DoRegister(uintptr_t(f), new DefaultReplayer<Signature>(m_deserializer, f),
+               ID);
+  }
+
+  /// Register a replayer that invokes a custom function with the same
+  /// signature as the replayed function.
+  template <typename Signature>
+  void Register(Signature *f, unsigned ID, Signature *g) {
+    DoRegister(uintptr_t(f),
+               new CustomReplayer<Signature>(m_deserializer, f, g), ID);
+  }
+
+  /// Replay functions from a file.
+  bool Replay(const FileSpec &file);
+
+  /// Returns the ID for a given function address.
+  unsigned GetID(uintptr_t addr) {
+    unsigned id = m_sbreplayers[addr].second;
+    assert(id != 0);
+    return id;
+  }
+
+private:
+  /// Initialize the registry by registering function.
+  void Init();
+
+  /// Register the given replayer for a function (and the ID mapping).
+  void DoRegister(uintptr_t RunID, SBReplayer *replayer, unsigned id) {
+    m_sbreplayers[RunID] = std::make_pair(replayer, id);
+    m_ids[id] = replayer;
+  }
+
+  // The deserializer is tightly coupled with the registry.
+  SBDeserializer m_deserializer;
+
+  /// Mapping of function addresses to replayers and their ID.
+  std::map<uintptr_t, std::pair<SBReplayer *, unsigned>> m_sbreplayers;
+
+  /// Mapping of IDs to replayer instances.
+  std::map<unsigned, SBReplayer *> m_ids;
+
+  /// Unique ID for every function registered with the registry.
+  unsigned m_id;
+};
+
+/// To be used as the "Runtime ID" of a constructor. It also invokes the
+/// constructor when called.
+template <typename Signature> struct construct;
+template <typename Class, typename... Args> struct construct<Class(Args...)> {
+  static Class *doit(Args... args) { return new Class(args...); }
+};
+
+/// To be used as the "Runtime ID" of a member function. It also invokes the
+/// member function when called.
+template <typename Signature> struct invoke;
+template <typename Result, typename Class, typename... Args>
+struct invoke<Result (Class::*)(Args...)> {
+  template <Result (Class::*m)(Args...)> struct method {
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
+  };
+};
+
+template <typename Result, typename Class, typename... Args>
+struct invoke<Result (Class::*)(Args...) const> {
+  template <Result (Class::*m)(Args...) const> struct method_const {
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
+  };
+};
+
+template <typename Class, typename... Args>
+struct invoke<void (Class::*)(Args...)> {
+  template <void (Class::*m)(Args...)> struct method {
+    static void doit(Class *c, Args... args) { (c->*m)(args...); }
+  };
+};
+
+/// Maps an object to an index for serialization. Indices are unique and
+/// incremented for every new object.
+///
+/// Indices start at 1 in order to differentiate with an invalid index (0) in
+/// the serialized buffer.
+class SBObjectToIndex {
+public:
+  SBObjectToIndex() : m_index(1) {}
+
+  template <typename T> unsigned GetIndexForObject(T *t) {
+    return GetIndexForObjectImpl((void *)t);
+  }
+
+private:
+  unsigned GetIndexForObjectImpl(void *object) {
+    auto it = m_mapping.find(object);
+    if (it == m_mapping.end())
+      m_mapping[object] = Increment();
+    return m_mapping[object];
+  }
+
+  unsigned Increment() {
+    std::lock_guard<std::mutex> guard(m_mutex);
+    return ++m_index;
+  }
+
+  unsigned m_index;
+  std::mutex m_mutex;
+  llvm::DenseMap<void *, unsigned> m_mapping;
+};
+
+/// Serializes functions, their arguments and their return type to a stream.
+class SBSerializer {
+public:
+  SBSerializer(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() {}
+
+private:
+  /// Serialize pointers. We need to differentiate between pointers to
+  /// fundamental types (in which case we serialize its value) and pointer to
+  /// objects (in which case we serialize their index).
+  template <typename T> void Serialize(T *t) {
+    if (std::is_fundamental<T>::value) {
+      Serialize(*t);
+    } else {
+      int idx = m_tracker.GetIndexForObject(t);
+      Serialize(idx);
+    }
+  }
+
+  /// Serialize references. We need to differentiate between references to
+  /// fundamental types (in which case we serialize its value) and references
+  /// to objects (in which case we serialize their index).
+  template <typename T> void Serialize(T &t) {
+    if (std::is_fundamental<T>::value) {
+      Serialize(t);
+    } else {
+      int idx = m_tracker.GetIndexForObject(&t);
+      Serialize(idx);
+    }
+  }
+
+  void Serialize(void *v) {
+    // Do nothing.
+  }
+
+  void Serialize(const char *t) {
+    m_stream << t;
+    m_stream.write(0x0);
+  }
+
+#define SB_SERIALIZER_POD(Type)                                                \
+  void Serialize(Type t) {                                                     \
+    m_stream.write(reinterpret_cast<const char *>(&t), sizeof(Type));          \
+  }
+
+  SB_SERIALIZER_POD(bool);
+  SB_SERIALIZER_POD(char);
+  SB_SERIALIZER_POD(double);
+  SB_SERIALIZER_POD(float);
+  SB_SERIALIZER_POD(int);
+  SB_SERIALIZER_POD(long long);
+  SB_SERIALIZER_POD(long);
+  SB_SERIALIZER_POD(short);
+  SB_SERIALIZER_POD(unsigned char);
+  SB_SERIALIZER_POD(unsigned int);
+  SB_SERIALIZER_POD(unsigned long long);
+  SB_SERIALIZER_POD(unsigned long);
+  SB_SERIALIZER_POD(unsigned short);
+
+  /// Serialization stream.
+  llvm::raw_ostream &m_stream;
+
+  /// Mapping of objects to indices.
+  SBObjectToIndex m_tracker;
+};
+
+class SBProvider : public Provider<SBProvider> {
+public:
+  SBProvider(const FileSpec &directory)
+      : Provider(directory),
+        m_stream(directory.CopyByAppendingPathComponent("sbapi.bin").GetPath(),
+                 m_ec, llvm::sys::fs::OpenFlags::F_None),
+        m_serializer(m_stream) {
+    m_info.name = "sbapi";
+    m_info.files.push_back("sbapi.bin");
+  }
+
+  SBSerializer &GetSerializer() { return m_serializer; }
+  static char ID;
+
+private:
+  std::error_code m_ec;
+  llvm::raw_fd_ostream m_stream;
+  SBSerializer m_serializer;
+};
+
+/// RAII object that tracks the 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.
+class SBRecorder {
+public:
+  SBRecorder(llvm::StringRef pretty_func = "")
+      : m_pretty_func(pretty_func), m_serializer(nullptr),
+        m_local_boundary(false), m_result_recorded(false) {
+    if (!g_global_boundary) {
+      g_global_boundary = true;
+      m_local_boundary = true;
+    }
+  }
+
+  ~SBRecorder() {
+    UpdateBoundary();
+    RecordOmittedResult();
+  }
+
+  void SetSerializer(SBSerializer &serializer) { m_serializer = &serializer; }
+
+  /// Records a single function call.
+  template <typename Result, typename... FArgs, typename... RArgs>
+  void Record(Result (*f)(FArgs...), const RArgs &... args) {
+    if (!ShouldCapture()) {
+      return;
+    }
+
+    unsigned id = SBRegistry::Instance().GetID(uintptr_t(f));
+
+    if (Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API))
+      log->Printf("Recording #%u '%s'", id, m_pretty_func.data());
+
+    m_serializer->SerializeAll(id);
+    m_serializer->SerializeAll(args...);
+  }
+
+  /// Records a single function call.
+  template <typename... Args>
+  void Record(void (*f)(Args...), const Args &... args) {
+    if (!ShouldCapture()) {
+      return;
+    }
+
+    unsigned id = SBRegistry::Instance().GetID(uintptr_t(f));
+
+    if (Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API))
+      log->Printf("Recording #%u '%s'", id, m_pretty_func.data());
+
+    m_serializer->SerializeAll(id);
+    m_serializer->SerializeAll(args...);
+  }
+
+  /// Record the result of a function call.
+  template <typename Result> Result RecordResult(const Result &r) {
+    UpdateBoundary();
+    if (ShouldCapture()) {
+      m_serializer->SerializeAll(r);
+      m_result_recorded = true;
+    }
+    return r;
+  }
+
+  /// Serialize an omitted return value.
+  void RecordOmittedResult() {
+    if (m_result_recorded)
+      return;
+    if (!ShouldCapture())
+      return;
+
+    m_serializer->SerializeAll(0);
+    m_result_recorded = true;
+  }
+
+private:
+  void UpdateBoundary() {
+    if (m_local_boundary) {
+      g_global_boundary = false;
+    }
+  }
+
+  bool ShouldCapture() { return m_serializer && m_local_boundary; }
+
+  /// Pretty function for logging.
+  llvm::StringRef m_pretty_func;
+
+  /// The serializer is set from the reproducer framework. If the serializer is
+  /// not set, we're not in recording mode.
+  SBSerializer *m_serializer;
+
+  /// Whether this function call was the one crossing the API boundary.
+  bool m_local_boundary;
+
+  /// Whether the return value was recorded explicitly.
+  bool m_result_recorded;
+
+  /// Whether we're currently across the API boundary.
+  static std::atomic<bool> g_global_boundary;
+};
+
+} // namespace repro
+} // namespace lldb_private
+
+#define SB_RECORD_CONSTRUCTOR(Class, Signature, ...)                           \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);          \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(&lldb_private::repro::construct<Class Signature>::doit, \
+                       __VA_ARGS__);                                           \
+    sb_recorder.RecordResult(this);                                            \
+  }
+
+#define SB_RECORD_CONSTRUCTOR_NO_ARGS(Class)                                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);          \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(&lldb_private::repro::construct<Class()>::doit);        \
+    sb_recorder.RecordResult(this);                                            \
+  }
+
+#define SB_RECORD_METHOD(Result, Class, Method, Signature, ...)                \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(&lldb_private::repro::invoke<Result(                    \
+                           Class::*) Signature>::method<&Class::Method>::doit, \
+                       this, __VA_ARGS__);                                     \
+  }
+
+#define SB_RECORD_METHOD_CONST(Result, Class, Method, Signature, ...)          \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(                                                        \
+        &lldb_private::repro::invoke<Result(                                   \
+            Class::*) Signature const>::method_const<&Class::Method>::doit,    \
+        this, __VA_ARGS__);                                                    \
+  }
+
+#define SB_RECORD_METHOD_NO_ARGS(Result, Class, Method)                        \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(&lldb_private::repro::invoke<Result (                   \
+                           Class::*)()>::method<&Class::Method>::doit,         \
+                       this);                                                  \
+  }
+
+#define SB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method)                  \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(                                                        \
+        &lldb_private::repro::invoke<Result (                                  \
+            Class::*)() const>::method_const<&Class::Method>::doit,            \
+        this);                                                                 \
+  }
+
+#define SB_RECORD_STATIC_METHOD(Result, Class, Method, Signature, ...)         \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(static_cast<Result(*) Signature>(&Class::Method),       \
+                       __VA_ARGS__);                                           \
+  }
+
+#define SB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method)                 \
+  lldb_private::repro::SBRecorder sb_recorder(__PRETTY_FUNCTION__);            \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.SetSerializer(                                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer());                  \
+    sb_recorder.Record(static_cast<Result (*)()>(&Class::Method));             \
+  }
+
+#define SB_RECORD_RESULT(Result) sb_recorder.RecordResult(Result);
+
+#endif
Index: source/API/SBReproducer.cpp
===================================================================
--- /dev/null
+++ source/API/SBReproducer.cpp
@@ -0,0 +1,215 @@
+//===-- SBReproducer.cpp ----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SBReproducerPrivate.h"
+
+#include "lldb/API/LLDB.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBReproducer.h"
+
+#include "lldb/Host/FileSystem.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::repro;
+
+#define REGISTER_CONSTRUCTOR(Class, Signature)                                 \
+  Register<Class * Signature>(&construct<Class Signature>::doit, m_id++)
+
+#define REGISTER_METHOD(Result, Class, Method, Signature)                      \
+  Register(&invoke<Result(Class::*) Signature>::method<&Class::Method>::doit,  \
+           m_id++)
+#define REGISTER_METHOD_CONST(Result, Class, Method, Signature)                \
+  Register(&invoke<Result(Class::*)                                            \
+                       Signature const>::method_const<&Class::Method>::doit,   \
+           m_id++)
+#define REGISTER_STATIC_METHOD(Result, Class, Method, Signature)               \
+  Register<Result Signature>(static_cast<Result(*) Signature>(&Class::Method), \
+                             m_id++)
+
+static FileSpec GetCommandFile() {
+  repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+  if (!loader) {
+    return {};
+  }
+
+  auto provider_info = loader->GetProviderInfo("command-interpreter");
+  if (!provider_info) {
+    return {};
+  }
+
+  if (provider_info->files.empty()) {
+    return {};
+  }
+
+  return loader->GetRoot().CopyByAppendingPathComponent(
+      provider_info->files.front());
+}
+
+static void SetInputFileHandleRedirect(SBDebugger *t, FILE *, bool) {
+  FileSpec fs = GetCommandFile();
+  if (!fs)
+    return;
+
+  FILE *f = FileSystem::Instance().Fopen(fs.GetPath().c_str(), "r");
+  if (f == nullptr)
+    return;
+  t->SetInputFileHandle(f, true);
+}
+
+static void SetFileHandleRedirect(SBDebugger *, FILE *, bool) {
+  // Do nothing.
+}
+
+void SBRegistry::Init() {
+  // SBFileSpec
+  {
+    REGISTER_CONSTRUCTOR(SBFileSpec, ());
+    REGISTER_CONSTRUCTOR(SBFileSpec, (const lldb::SBFileSpec &));
+    REGISTER_CONSTRUCTOR(SBFileSpec, (const char *));
+    REGISTER_CONSTRUCTOR(SBFileSpec, (const char *, bool));
+
+    REGISTER_METHOD_CONST(bool, SBFileSpec, IsValid, ());
+    REGISTER_METHOD_CONST(bool, SBFileSpec, Exists, ());
+    REGISTER_METHOD_CONST(const char *, SBFileSpec, GetFilename, ());
+    REGISTER_METHOD_CONST(const char *, SBFileSpec, GetDirectory, ());
+    REGISTER_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t));
+    REGISTER_METHOD_CONST(bool, SBFileSpec, GetDescription, (lldb::SBStream &));
+
+    REGISTER_METHOD(const SBFileSpec &,
+                    SBFileSpec, operator=,(const lldb::SBFileSpec &));
+    REGISTER_METHOD(bool, SBFileSpec, ResolveExecutableLocation, ());
+    REGISTER_METHOD(void, SBFileSpec, SetFilename, (const char *));
+    REGISTER_METHOD(void, SBFileSpec, SetDirectory, (const char *));
+    REGISTER_METHOD(void, SBFileSpec, AppendPathComponent, (const char *));
+
+    REGISTER_STATIC_METHOD(int, SBFileSpec, ResolvePath,
+                           (const char *, char *, size_t));
+  }
+
+  // SBHostOS
+  { REGISTER_STATIC_METHOD(SBFileSpec, SBHostOS, GetUserHomeDirectory, ()); }
+
+  // SBDebugger
+  {
+    REGISTER_CONSTRUCTOR(SBDebugger, ());
+    REGISTER_CONSTRUCTOR(SBDebugger, (const DebuggerSP &));
+    REGISTER_CONSTRUCTOR(SBDebugger, (const SBDebugger &));
+
+    REGISTER_METHOD(SBCommandInterpreter, SBDebugger, GetCommandInterpreter,
+                    ());
+    REGISTER_METHOD(SBDebugger &, SBDebugger, operator=,(const SBDebugger &));
+    REGISTER_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool));
+    REGISTER_METHOD(void, SBDebugger, SkipAppInitFiles, (bool));
+    REGISTER_METHOD(bool, SBDebugger, GetAsync, ());
+    REGISTER_METHOD(void, SBDebugger, SetAsync, (bool));
+    REGISTER_METHOD(void, SBDebugger, RunCommandInterpreter,
+                    (bool, bool, lldb::SBCommandInterpreterRunOptions &, int &,
+                     bool &, bool &));
+
+    // Custom implementation.
+    Register(&invoke<void (SBDebugger::*)(
+                 FILE *, bool)>::method<&SBDebugger::SetInputFileHandle>::doit,
+             m_id++, &SetInputFileHandleRedirect);
+    Register(&invoke<void (SBDebugger::*)(
+                 FILE *, bool)>::method<&SBDebugger::SetErrorFileHandle>::doit,
+             m_id++, &SetFileHandleRedirect);
+    Register(&invoke<void (SBDebugger::*)(
+                 FILE *, bool)>::method<&SBDebugger::SetOutputFileHandle>::doit,
+             m_id++, &SetFileHandleRedirect);
+
+    REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create, ());
+    REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create, (bool));
+
+    // FIXME: Why is this an issue?
+#if 0
+    REGISTER_STATIC_METHOD(SBDebugger, SBDebugger, Create,
+                           (bool, lldb::LogOutputCallback, void *));
+#endif
+  }
+
+  // SBCommandInterpreter
+  {
+    REGISTER_CONSTRUCTOR(SBCommandInterpreter,
+                         (lldb_private::CommandInterpreter *));
+    REGISTER_CONSTRUCTOR(SBCommandInterpreter, (SBCommandInterpreter &));
+    REGISTER_CONSTRUCTOR(SBCommandInterpreter, (const SBCommandInterpreter &));
+
+    REGISTER_METHOD_CONST(bool, SBCommandInterpreter, IsValid, ());
+
+    REGISTER_METHOD(void, SBCommandInterpreter, SourceInitFileInHomeDirectory,
+                    (SBCommandReturnObject &));
+    REGISTER_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit, (bool));
+    REGISTER_METHOD(int, SBCommandInterpreter, GetQuitStatus, ());
+  }
+
+  // SBCommandInterpreterRunOptions
+  {
+    REGISTER_CONSTRUCTOR(SBCommandInterpreterRunOptions, ());
+    REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnError,
+                    (bool));
+    REGISTER_METHOD(void, SBCommandInterpreterRunOptions, SetStopOnCrash,
+                    (bool));
+  }
+
+  // SBCommandReturnObject
+  { REGISTER_CONSTRUCTOR(SBCommandReturnObject, ()); }
+}
+
+bool SBReproducer::Replay() const {
+  repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+  if (!loader) {
+    return false;
+  }
+
+  llvm::Optional<repro::ProviderInfo> info = loader->GetProviderInfo("sbapi");
+  if (!info) {
+    return false;
+  }
+
+  FileSpec file(loader->GetRoot());
+  file.AppendPathComponent(info->files.front());
+
+  SBRegistry::Instance().Replay(file);
+
+  return true;
+}
+
+bool SBRegistry::Replay(const FileSpec &file) {
+  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+  if (auto err = error_or_file.getError())
+    return false;
+
+  m_deserializer.LoadBuffer((*error_or_file)->getBuffer());
+  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
+
+  while (m_deserializer.HasData(1)) {
+    unsigned id = m_deserializer.Deserialize<unsigned>();
+    if (log)
+      log->Printf("Replaying function #%u", id);
+    m_ids[id]->operator()();
+  }
+
+  return true;
+}
+
+template <> const char *SBDeserializer::Deserialize<const char *>() {
+  auto pos = m_buffer.find('\0', m_offset);
+  if (pos == llvm::StringRef::npos)
+    return nullptr;
+  size_t begin = m_offset;
+  m_offset = pos + 1;
+  return m_buffer.data() + begin;
+}
+
+std::atomic<bool> lldb_private::repro::SBRecorder::g_global_boundary;
+char lldb_private::repro::SBProvider::ID = 0;
Index: source/API/SBHostOS.cpp
===================================================================
--- source/API/SBHostOS.cpp
+++ source/API/SBHostOS.cpp
@@ -11,8 +11,10 @@
 #include "Plugins/ScriptInterpreter/Python/lldb-python.h"
 #endif
 
+#include "SBReproducerPrivate.h"
 #include "lldb/API/SBError.h"
 #include "lldb/API/SBHostOS.h"
+#include "lldb/API/SBReproducer.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
@@ -83,6 +85,7 @@
 }
 
 SBFileSpec SBHostOS::GetUserHomeDirectory() {
+  SB_RECORD_STATIC_METHOD_NO_ARGS(SBFileSpec, SBHostOS, GetUserHomeDirectory);
   SBFileSpec sb_fspec;
 
   llvm::SmallString<64> home_dir_path;
@@ -91,7 +94,7 @@
   FileSystem::Instance().Resolve(homedir);
 
   sb_fspec.SetFileSpec(homedir);
-  return sb_fspec;
+  return SB_RECORD_RESULT(sb_fspec);
 }
 
 lldb::thread_t SBHostOS::ThreadCreate(const char *name,
Index: source/API/SBFileSpec.cpp
===================================================================
--- source/API/SBFileSpec.cpp
+++ source/API/SBFileSpec.cpp
@@ -10,7 +10,9 @@
 #include <inttypes.h>
 #include <limits.h>
 
+#include "SBReproducerPrivate.h"
 #include "lldb/API/SBFileSpec.h"
+#include "lldb/API/SBReproducer.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/PosixApi.h"
@@ -23,21 +25,27 @@
 using namespace lldb;
 using namespace lldb_private;
 
-SBFileSpec::SBFileSpec() : m_opaque_ap(new lldb_private::FileSpec()) {}
+SBFileSpec::SBFileSpec() : m_opaque_ap(new lldb_private::FileSpec()) {
+  SB_RECORD_CONSTRUCTOR_NO_ARGS(SBFileSpec);
+}
 
 SBFileSpec::SBFileSpec(const SBFileSpec &rhs)
-    : m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) {}
+    : m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) {
+  SB_RECORD_CONSTRUCTOR(SBFileSpec, (const SBFileSpec &), rhs);
+}
 
 SBFileSpec::SBFileSpec(const lldb_private::FileSpec &fspec)
     : m_opaque_ap(new lldb_private::FileSpec(fspec)) {}
 
 // Deprecated!!!
 SBFileSpec::SBFileSpec(const char *path) : m_opaque_ap(new FileSpec(path)) {
+  SB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *), path);
   FileSystem::Instance().Resolve(*m_opaque_ap);
 }
 
 SBFileSpec::SBFileSpec(const char *path, bool resolve)
     : m_opaque_ap(new FileSpec(path)) {
+  SB_RECORD_CONSTRUCTOR(SBFileSpec, (const char *, bool), path, resolve);
   if (resolve)
     FileSystem::Instance().Resolve(*m_opaque_ap);
 }
@@ -45,14 +53,20 @@
 SBFileSpec::~SBFileSpec() {}
 
 const SBFileSpec &SBFileSpec::operator=(const SBFileSpec &rhs) {
+  SB_RECORD_METHOD(const SBFileSpec &,
+                   SBFileSpec, operator=,(const SBFileSpec &), rhs);
   if (this != &rhs)
     *m_opaque_ap = *rhs.m_opaque_ap;
   return *this;
 }
 
-bool SBFileSpec::IsValid() const { return m_opaque_ap->operator bool(); }
+bool SBFileSpec::IsValid() const {
+  SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, IsValid);
+  return m_opaque_ap->operator bool();
+}
 
 bool SBFileSpec::Exists() const {
+  SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBFileSpec, IsValid);
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   bool result = FileSystem::Instance().Exists(*m_opaque_ap);
@@ -66,11 +80,15 @@
 }
 
 bool SBFileSpec::ResolveExecutableLocation() {
+  SB_RECORD_METHOD_NO_ARGS(bool, SBFileSpec, ResolveExecutableLocation);
   return FileSystem::Instance().ResolveExecutableLocation(*m_opaque_ap);
 }
 
 int SBFileSpec::ResolvePath(const char *src_path, char *dst_path,
                             size_t dst_len) {
+  SB_RECORD_STATIC_METHOD(int, SBFileSpec, ResolvePath,
+                          (const char *, char *, size_t), src_path, dst_path,
+                          dst_len);
   llvm::SmallString<64> result(src_path);
   FileSystem::Instance().Resolve(result);
   ::snprintf(dst_path, dst_len, "%s", result.c_str());
@@ -78,6 +96,7 @@
 }
 
 const char *SBFileSpec::GetFilename() const {
+  SB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetFilename);
   const char *s = m_opaque_ap->GetFilename().AsCString();
 
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
@@ -94,6 +113,7 @@
 }
 
 const char *SBFileSpec::GetDirectory() const {
+  SB_RECORD_METHOD_CONST_NO_ARGS(const char *, SBFileSpec, GetDirectory);
   FileSpec directory{*m_opaque_ap};
   directory.GetFilename().Clear();
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
@@ -110,6 +130,7 @@
 }
 
 void SBFileSpec::SetFilename(const char *filename) {
+  SB_RECORD_METHOD(void, SBFileSpec, SetFilename, (const char *), filename);
   if (filename && filename[0])
     m_opaque_ap->GetFilename().SetCString(filename);
   else
@@ -117,6 +138,7 @@
 }
 
 void SBFileSpec::SetDirectory(const char *directory) {
+  SB_RECORD_METHOD(void, SBFileSpec, SetDirectory, (const char *), directory);
   if (directory && directory[0])
     m_opaque_ap->GetDirectory().SetCString(directory);
   else
@@ -124,6 +146,8 @@
 }
 
 uint32_t SBFileSpec::GetPath(char *dst_path, size_t dst_len) const {
+  SB_RECORD_METHOD_CONST(uint32_t, SBFileSpec, GetPath, (char *, size_t),
+                         dst_path, dst_len);
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   uint32_t result = m_opaque_ap->GetPath(dst_path, dst_len);
@@ -158,6 +182,8 @@
 }
 
 bool SBFileSpec::GetDescription(SBStream &description) const {
+  SB_RECORD_METHOD_CONST(bool, SBFileSpec, GetDescription, (SBStream &),
+                         description);
   Stream &strm = description.ref();
   char path[PATH_MAX];
   if (m_opaque_ap->GetPath(path, sizeof(path)))
@@ -166,5 +192,6 @@
 }
 
 void SBFileSpec::AppendPathComponent(const char *fn) {
+  SB_RECORD_METHOD(void, SBFileSpec, AppendPathComponent, (const char *), fn);
   m_opaque_ap->AppendPathComponent(fn);
 }
Index: source/API/SBDebugger.cpp
===================================================================
--- source/API/SBDebugger.cpp
+++ source/API/SBDebugger.cpp
@@ -7,10 +7,11 @@
 //
 //===----------------------------------------------------------------------===//
 
-
+#include "SBReproducerPrivate.h"
 #include "SystemInitializerFull.h"
 
 #include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBReproducer.h"
 
 #include "lldb/lldb-private.h"
 
@@ -108,14 +109,18 @@
 
 bool SBInputReader::IsActive() const { return false; }
 
-SBDebugger::SBDebugger() = default;
+SBDebugger::SBDebugger() { SB_RECORD_CONSTRUCTOR_NO_ARGS(SBDebugger); }
 
 SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp)
-    : m_opaque_sp(debugger_sp) {}
+    : m_opaque_sp(debugger_sp) {
+  SB_RECORD_CONSTRUCTOR(SBDebugger, (const DebuggerSP &), debugger_sp);
+}
 
-SBDebugger::SBDebugger(const SBDebugger &rhs) : m_opaque_sp(rhs.m_opaque_sp) {}
+SBDebugger::SBDebugger(const SBDebugger &rhs) : m_opaque_sp(rhs.m_opaque_sp) {
+  SB_RECORD_CONSTRUCTOR(SBDebugger, (const SBDebugger &), rhs);
+}
 
-SBDebugger::~SBDebugger() = default;
+SBDebugger::~SBDebugger() {}
 
 SBDebugger &SBDebugger::operator=(const SBDebugger &rhs) {
   if (this != &rhs) {
@@ -160,17 +165,24 @@
 }
 
 SBDebugger SBDebugger::Create() {
-  return SBDebugger::Create(false, nullptr, nullptr);
+  SB_RECORD_STATIC_METHOD_NO_ARGS(SBDebugger, SBDebugger, Create);
+  return SB_RECORD_RESULT(SBDebugger::Create(false, nullptr, nullptr));
 }
 
 SBDebugger SBDebugger::Create(bool source_init_files) {
-  return SBDebugger::Create(source_init_files, nullptr, nullptr);
+  SB_RECORD_STATIC_METHOD(SBDebugger, SBDebugger, Create, (bool),
+                          source_init_files);
+  return SB_RECORD_RESULT(
+      SBDebugger::Create(source_init_files, nullptr, nullptr));
 }
 
 SBDebugger SBDebugger::Create(bool source_init_files,
                               lldb::LogOutputCallback callback, void *baton)
 
 {
+  SB_RECORD_STATIC_METHOD(SBDebugger, SBDebugger, Create,
+                          (bool, lldb::LogOutputCallback, void *),
+                          source_init_files, callback, baton);
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   SBDebugger debugger;
@@ -203,7 +215,7 @@
     interp.get()->SkipLLDBInitFiles(true);
     interp.get()->SkipAppInitFiles(true);
   }
-  return debugger;
+  return SB_RECORD_RESULT(debugger);
 }
 
 void SBDebugger::Destroy(SBDebugger &debugger) {
@@ -242,20 +254,24 @@
 bool SBDebugger::IsValid() const { return m_opaque_sp.get() != nullptr; }
 
 void SBDebugger::SetAsync(bool b) {
+  SB_RECORD_METHOD(void, SBDebugger, SetAsync, (bool), b);
   if (m_opaque_sp)
     m_opaque_sp->SetAsyncExecution(b);
 }
 
 bool SBDebugger::GetAsync() {
+  SB_RECORD_METHOD_NO_ARGS(bool, SBDebugger, GetAsync);
   return (m_opaque_sp ? m_opaque_sp->GetAsyncExecution() : false);
 }
 
 void SBDebugger::SkipLLDBInitFiles(bool b) {
+  SB_RECORD_METHOD(void, SBDebugger, SkipLLDBInitFiles, (bool), b);
   if (m_opaque_sp)
     m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles(b);
 }
 
 void SBDebugger::SkipAppInitFiles(bool b) {
+  SB_RECORD_METHOD(void, SBDebugger, SkipAppInitFiles, (bool), b);
   if (m_opaque_sp)
     m_opaque_sp->GetCommandInterpreter().SkipAppInitFiles(b);
 }
@@ -264,6 +280,8 @@
 // of problems; don't want users trying to switch modes in the middle of a
 // debugging session.
 void SBDebugger::SetInputFileHandle(FILE *fh, bool transfer_ownership) {
+  SB_RECORD_METHOD(void, SBDebugger, SetInputFileHandle, (FILE *, bool), fh,
+                   transfer_ownership);
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   if (log)
@@ -277,6 +295,8 @@
 }
 
 void SBDebugger::SetOutputFileHandle(FILE *fh, bool transfer_ownership) {
+  SB_RECORD_METHOD(void, SBDebugger, SetOutputFileHandle, (FILE *, bool), fh,
+                   transfer_ownership);
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   if (log)
@@ -290,6 +310,8 @@
 }
 
 void SBDebugger::SetErrorFileHandle(FILE *fh, bool transfer_ownership) {
+  SB_RECORD_METHOD(void, SBDebugger, SetErrorFileHandle, (FILE *, bool), fh,
+                   transfer_ownership);
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   if (log)
@@ -339,6 +361,8 @@
     m_opaque_sp->RestoreInputTerminalState();
 }
 SBCommandInterpreter SBDebugger::GetCommandInterpreter() {
+  SB_RECORD_METHOD_NO_ARGS(SBCommandInterpreter, SBDebugger,
+                           GetCommandInterpreter);
   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   SBCommandInterpreter sb_interpreter;
@@ -351,7 +375,7 @@
         static_cast<void *>(m_opaque_sp.get()),
         static_cast<void *>(sb_interpreter.get()));
 
-  return sb_interpreter;
+  return SB_RECORD_RESULT(sb_interpreter);
 }
 
 void SBDebugger::HandleCommand(const char *command) {
@@ -943,6 +967,11 @@
                                        bool &stopped_for_crash)
 
 {
+  SB_RECORD_METHOD(void, SBDebugger, RunCommandInterpreter,
+                   (bool, bool, lldb::SBCommandInterpreterRunOptions &, int &,
+                    bool &, bool &),
+                   auto_handle_events, spawn_thread, options, num_errors,
+                   quit_requested, stopped_for_crash);
   if (m_opaque_sp) {
     CommandInterpreter &interp = m_opaque_sp->GetCommandInterpreter();
     interp.RunCommandInterpreter(auto_handle_events, spawn_thread,
Index: source/API/SBCommandReturnObject.cpp
===================================================================
--- source/API/SBCommandReturnObject.cpp
+++ source/API/SBCommandReturnObject.cpp
@@ -8,7 +8,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "lldb/API/SBCommandReturnObject.h"
+#include "SBReproducerPrivate.h"
 #include "lldb/API/SBError.h"
+#include "lldb/API/SBReproducer.h"
 #include "lldb/API/SBStream.h"
 
 #include "lldb/Interpreter/CommandReturnObject.h"
@@ -20,7 +22,10 @@
 using namespace lldb_private;
 
 SBCommandReturnObject::SBCommandReturnObject()
-    : m_opaque_ap(new CommandReturnObject()) {}
+    : m_opaque_ap(new CommandReturnObject()) {
+
+  SB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandReturnObject);
+}
 
 SBCommandReturnObject::SBCommandReturnObject(const SBCommandReturnObject &rhs)
     : m_opaque_ap() {
Index: source/API/SBCommandInterpreter.cpp
===================================================================
--- source/API/SBCommandInterpreter.cpp
+++ source/API/SBCommandInterpreter.cpp
@@ -7,6 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "SBReproducerPrivate.h"
 #include "lldb/lldb-types.h"
 
 #include "lldb/Interpreter/CommandInterpreter.h"
@@ -22,6 +23,7 @@
 #include "lldb/API/SBExecutionContext.h"
 #include "lldb/API/SBListener.h"
 #include "lldb/API/SBProcess.h"
+#include "lldb/API/SBReproducer.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
 #include "lldb/API/SBTarget.h"
@@ -31,6 +33,7 @@
 
 SBCommandInterpreterRunOptions::SBCommandInterpreterRunOptions() {
   m_opaque_up.reset(new CommandInterpreterRunOptions());
+  SB_RECORD_CONSTRUCTOR_NO_ARGS(SBCommandInterpreterRunOptions);
 }
 
 SBCommandInterpreterRunOptions::~SBCommandInterpreterRunOptions() = default;
@@ -129,6 +132,8 @@
 
 SBCommandInterpreter::SBCommandInterpreter(CommandInterpreter *interpreter)
     : m_opaque_ptr(interpreter) {
+  SB_RECORD_CONSTRUCTOR(SBCommandInterpreter, (CommandInterpreter *),
+                        interpreter);
   Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
 
   if (log)
@@ -139,7 +144,10 @@
 }
 
 SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs)
-    : m_opaque_ptr(rhs.m_opaque_ptr) {}
+    : m_opaque_ptr(rhs.m_opaque_ptr) {
+  SB_RECORD_CONSTRUCTOR(SBCommandInterpreter, (const SBCommandInterpreter &),
+                        rhs);
+}
 
 SBCommandInterpreter::~SBCommandInterpreter() = default;
 
@@ -149,7 +157,10 @@
   return *this;
 }
 
-bool SBCommandInterpreter::IsValid() const { return m_opaque_ptr != nullptr; }
+bool SBCommandInterpreter::IsValid() const {
+  SB_RECORD_METHOD_CONST_NO_ARGS(bool, SBCommandInterpreter, IsValid);
+  return m_opaque_ptr != nullptr;
+}
 
 bool SBCommandInterpreter::CommandExists(const char *cmd) {
   return (((cmd != nullptr) && IsValid()) ? m_opaque_ptr->CommandExists(cmd)
@@ -407,6 +418,8 @@
 }
 
 void SBCommandInterpreter::AllowExitCodeOnQuit(bool allow) {
+  SB_RECORD_METHOD(void, SBCommandInterpreter, AllowExitCodeOnQuit, (bool),
+                   allow);
   if (m_opaque_ptr)
     m_opaque_ptr->AllowExitCodeOnQuit(allow);
 }
@@ -419,6 +432,7 @@
 }
 
 int SBCommandInterpreter::GetQuitStatus() {
+  SB_RECORD_METHOD_NO_ARGS(int, SBCommandInterpreter, GetQuitStatus);
   bool exited = false;
   return (m_opaque_ptr ? m_opaque_ptr->GetQuitExitCode(exited) : 0);
 }
@@ -449,6 +463,8 @@
 
 void SBCommandInterpreter::SourceInitFileInHomeDirectory(
     SBCommandReturnObject &result) {
+  SB_RECORD_METHOD(void, SBCommandInterpreter, SourceInitFileInHomeDirectory,
+                   (SBCommandReturnObject &), result);
   result.Clear();
   if (IsValid()) {
     TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget());
Index: source/API/CMakeLists.txt
===================================================================
--- source/API/CMakeLists.txt
+++ source/API/CMakeLists.txt
@@ -50,6 +50,7 @@
   SBProcessInfo.cpp
   SBQueue.cpp
   SBQueueItem.cpp
+  SBReproducer.cpp
   SBSection.cpp
   SBSourceManager.cpp
   SBStream.cpp
Index: include/lldb/Utility/Reproducer.h
===================================================================
--- include/lldb/Utility/Reproducer.h
+++ include/lldb/Utility/Reproducer.h
@@ -70,6 +70,7 @@
 
   /// Every provider keeps track of its own files.
   ProviderInfo m_info;
+
 private:
   /// Every provider knows where to dump its potential files.
   FileSpec m_root;
Index: include/lldb/API/SBReproducer.h
===================================================================
--- /dev/null
+++ include/lldb/API/SBReproducer.h
@@ -0,0 +1,24 @@
+//===-- SBReproducer.h ------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SBREPRODUCER_H
+#define LLDB_API_SBREPRODUCER_H
+
+#include "lldb/lldb-defines.h"
+
+namespace lldb {
+
+class LLDB_API SBReproducer {
+public:
+  bool Replay() const;
+};
+
+} // namespace lldb
+
+#endif
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
  • [Lldb-commits] [PATCH] ... Jonas Devlieghere via Phabricator via lldb-commits

Reply via email to