JDevlieghere updated this revision to Diff 185208.
JDevlieghere added a comment.

Add context


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

https://reviews.llvm.org/D56322

Files:
  include/lldb/API/SBReproducer.h
  include/lldb/Utility/ReproducerInstrumentation.h
  source/API/CMakeLists.txt
  source/API/SBReproducer.cpp
  source/API/SBReproducerPrivate.h
  source/Utility/ReproducerInstrumentation.cpp
  tools/driver/Driver.cpp
  unittests/Utility/ReproducerInstrumentationTest.cpp

Index: unittests/Utility/ReproducerInstrumentationTest.cpp
===================================================================
--- unittests/Utility/ReproducerInstrumentationTest.cpp
+++ unittests/Utility/ReproducerInstrumentationTest.cpp
@@ -9,12 +9,24 @@
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+#include <cmath>
+#include <limits>
+
 #include "lldb/Utility/ReproducerInstrumentation.h"
 
 using namespace lldb_private;
 using namespace lldb_private::repro;
 
-namespace {
+#define SB_REGISTER_CONSTRUCTOR(Class, Signature)                              \
+  Register<Class * Signature>(&construct<Class Signature>::doit)
+#define SB_REGISTER_METHOD(Result, Class, Method, Signature)                   \
+  Register(&invoke<Result(Class::*) Signature>::method<&Class::Method>::doit)
+#define SB_REGISTER_METHOD_CONST(Result, Class, Method, Signature)             \
+  Register(&invoke<Result(Class::*)                                            \
+                       Signature const>::method_const<&Class::Method>::doit)
+#define SB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature)            \
+  Register<Result Signature>(static_cast<Result(*) Signature>(&Class::Method))
+
 struct Foo {
   int m = 1;
 };
@@ -40,7 +52,166 @@
   unsigned long l = 8;
   unsigned short m = 9;
 };
-} // namespace
+class TestingRegistry : public Registry {
+public:
+  TestingRegistry();
+};
+
+template <typename T> bool Equals(T LHS, T RHS) {
+  return std::fabs(RHS - LHS) < std::numeric_limits<T>::epsilon();
+}
+
+static Serializer g_serializer;
+static TestingRegistry g_registry;
+
+#define SB_RECORD_CONSTRUCTOR(Class, Signature, ...)                           \
+  lldb_private::repro::Recorder sb_recorder(g_serializer, g_registry,          \
+                                            LLVM_PRETTY_FUNCTION);             \
+  sb_recorder.Record(&lldb_private::repro::construct<Class Signature>::doit,   \
+                     __VA_ARGS__);                                             \
+  sb_recorder.RecordResult(this);
+
+#define SB_RECORD_CONSTRUCTOR_NO_ARGS(Class)                                   \
+  lldb_private::repro::Recorder sb_recorder(g_serializer, g_registry,          \
+                                            LLVM_PRETTY_FUNCTION);             \
+  sb_recorder.Record(&lldb_private::repro::construct<Class()>::doit);          \
+  sb_recorder.RecordResult(this);
+
+#define SB_RECORD_METHOD(Result, Class, Method, Signature, ...)                \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  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, ...)          \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  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)                        \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  sb_recorder->Record(&lldb_private::repro::invoke<Result (                    \
+                          Class::*)()>::method<&Class::Method>::doit,          \
+                      this);
+
+#define SB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method)                  \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  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, ...)         \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  sb_recorder->Record(static_cast<Result(*) Signature>(&Class::Method),        \
+                      __VA_ARGS__);
+
+#define SB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method)                 \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  sb_recorder.emplace(g_serializer, g_registry, LLVM_PRETTY_FUNCTION);         \
+  sb_recorder->Record(static_cast<Result (*)()>(&Class::Method));
+
+#define SB_RECORD_RESULT(Result)                                               \
+  sb_recorder ? sb_recorder->RecordResult(Result) : Result;
+
+class InstrumentedFoo {
+public:
+  InstrumentedFoo();
+  void A(int a);
+  void B(int &b) const;
+  int C(float *c);
+  int D(const char *d) const;
+  static void E(double e);
+  static int F();
+
+  // No instrumented.
+  static void Reset() {
+    g_a = 0;
+    g_b = 0;
+    g_c = 0;
+    g_d = "";
+    g_e = 0;
+    g_f = false;
+
+    EXPECT_EQ(g_a, 0);
+    EXPECT_EQ(g_b, 0);
+    EXPECT_EQ(g_c, 0);
+    EXPECT_EQ(g_d, "");
+    EXPECT_EQ(g_e, 0);
+    EXPECT_EQ(g_f, false);
+  }
+
+  // Members are all static because we have no handle into the replayed object.
+  static int g_a;
+  static int g_b;
+  static float g_c;
+  static std::string g_d;
+  static double g_e;
+  static bool g_f;
+};
+
+int InstrumentedFoo::g_a = 0;
+int InstrumentedFoo::g_b = 0;
+float InstrumentedFoo::g_c = 0;
+std::string InstrumentedFoo::g_d = "";
+double InstrumentedFoo::g_e = 0;
+bool InstrumentedFoo::g_f = false;
+
+InstrumentedFoo::InstrumentedFoo() {
+  SB_RECORD_CONSTRUCTOR_NO_ARGS(InstrumentedFoo);
+}
+
+void InstrumentedFoo::A(int a) {
+  SB_RECORD_METHOD(void, InstrumentedFoo, A, (int), a);
+  g_a = a;
+}
+
+void InstrumentedFoo::B(int &b) const {
+  SB_RECORD_METHOD_CONST(void, InstrumentedFoo, B, (int &), b);
+  g_b = b;
+}
+
+int InstrumentedFoo::C(float *c) {
+  SB_RECORD_METHOD(int, InstrumentedFoo, C, (float *), c);
+  g_c = *c;
+  return 1;
+}
+
+int InstrumentedFoo::D(const char *d) const {
+  SB_RECORD_METHOD_CONST(int, InstrumentedFoo, D, (const char *), d);
+  g_d = std::string(d);
+  return 2;
+}
+
+void InstrumentedFoo::E(double e) {
+  SB_RECORD_STATIC_METHOD(void, InstrumentedFoo, E, (double), e);
+  g_e = e;
+}
+
+int InstrumentedFoo::F() {
+  SB_RECORD_STATIC_METHOD_NO_ARGS(int, InstrumentedFoo, F);
+  g_f = true;
+  return 3;
+}
+
+class InstrumentedBar {};
+
+TestingRegistry::TestingRegistry() {
+  SB_REGISTER_CONSTRUCTOR(InstrumentedFoo, ());
+  SB_REGISTER_METHOD(void, InstrumentedFoo, A, (int));
+  SB_REGISTER_METHOD_CONST(void, InstrumentedFoo, B, (int &));
+  SB_REGISTER_METHOD(int, InstrumentedFoo, C, (float *));
+  SB_REGISTER_METHOD_CONST(int, InstrumentedFoo, D, (const char *));
+  SB_REGISTER_STATIC_METHOD(void, InstrumentedFoo, E, (double));
+  SB_REGISTER_STATIC_METHOD(int, InstrumentedFoo, F, ());
+}
 
 static const Pod p;
 
@@ -206,3 +377,41 @@
   EXPECT_EQ(foo, deserializer.Deserialize<Foo &>());
   EXPECT_EQ(bar, deserializer.Deserialize<Bar &>());
 }
+
+TEST(RecordReplayTest, InstrumentedFoo) {
+  std::string str;
+  llvm::raw_string_ostream os(str);
+  g_serializer = Serializer(os);
+
+  int b = 200;
+  float c = 300.3;
+  double e = 400.4;
+
+  InstrumentedFoo foo;
+  foo.A(100);
+  foo.B(b);
+  foo.C(&c);
+  foo.D("bar");
+
+  InstrumentedFoo::E(e);
+  InstrumentedFoo::F();
+
+  EXPECT_EQ(foo.g_a, 100);
+  EXPECT_EQ(foo.g_b, 200);
+  EXPECT_TRUE(Equals(foo.g_c, c));
+  EXPECT_EQ(foo.g_d, "bar");
+  EXPECT_TRUE(Equals(foo.g_e, e));
+  EXPECT_EQ(foo.g_f, true);
+
+  InstrumentedFoo::Reset();
+
+  TestingRegistry registry;
+  registry.Replay(os.str());
+
+  EXPECT_EQ(foo.g_a, 100);
+  EXPECT_EQ(foo.g_b, 200);
+  EXPECT_TRUE(Equals(foo.g_c, c));
+  EXPECT_EQ(foo.g_d, "bar");
+  EXPECT_TRUE(Equals(foo.g_e, e));
+  EXPECT_EQ(foo.g_f, true);
+}
Index: tools/driver/Driver.cpp
===================================================================
--- tools/driver/Driver.cpp
+++ tools/driver/Driver.cpp
@@ -13,6 +13,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"
 
@@ -888,8 +889,10 @@
                          << '\n';
   }
 
-  SBInitializerOptions options;
+  // Remember if we're in replay mode for later.
+  bool replay = false;
 
+  SBInitializerOptions options;
   if (auto *arg = input_args.getLastArg(OPT_capture)) {
     auto arg_value = arg->getValue();
     options.SetReproducerPath(arg_value);
@@ -900,6 +903,7 @@
     auto arg_value = arg->getValue();
     options.SetReplayReproducer(true);
     options.SetReproducerPath(arg_value);
+    replay = true;
   }
 
   SBError error = SBDebugger::Initialize(options);
@@ -909,6 +913,14 @@
     return 1;
   }
 
+  if (replay) {
+    SBReproducer reproducer;
+    if (!reproducer.Replay()) {
+      WithColor::error() << "something went wrong running the reporducer.\n";
+    }
+    return 0;
+  }
+
   SBHostOS::ThreadCreated("<lldb.driver.main-thread>");
 
   signal(SIGINT, sigint_handler);
Index: source/Utility/ReproducerInstrumentation.cpp
===================================================================
--- source/Utility/ReproducerInstrumentation.cpp
+++ source/Utility/ReproducerInstrumentation.cpp
@@ -34,11 +34,61 @@
   return str;
 }
 
+bool Registry::Replay(const FileSpec &file) {
+  auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
+  if (auto err = error_or_file.getError())
+    return false;
+
+  return Replay((*error_or_file)->getBuffer());
+}
+
+bool Registry::Replay(llvm::StringRef buffer) {
+  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
+
+  Deserializer deserializer(buffer);
+  while (deserializer.HasData(1)) {
+    unsigned id = deserializer.Deserialize<unsigned>();
+    LLDB_LOG(log, "Replaying function #{0}", id);
+    m_ids[id]->operator()(deserializer);
+  }
+
+  return true;
+}
+
+void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer) {
+  const unsigned id = m_replayers.size() + 1;
+  assert(m_replayers.find(RunID) == m_replayers.end());
+  m_replayers[RunID] = std::make_pair(std::move(replayer), id);
+  m_ids[id] = m_replayers[RunID].first.get();
+}
+
+unsigned Registry::GetID(uintptr_t addr) {
+  unsigned id = m_replayers[addr].second;
+  assert(id != 0 && "Forgot to add function to registry?");
+  return id;
+}
+
 unsigned ObjectToIndex::GetIndexForObjectImpl(void *object) {
-  std::lock_guard<std::mutex> guard(m_mutex);
   unsigned index = m_mapping.size() + 1;
   auto it = m_mapping.find(object);
   if (it == m_mapping.end())
     m_mapping[object] = index;
   return m_mapping[object];
 }
+
+Recorder::Recorder(Serializer &serializer, Registry &registry,
+                   llvm::StringRef pretty_func)
+    : m_pretty_func(pretty_func), m_serializer(serializer),
+      m_registry(registry), m_local_boundary(false), m_result_recorded(true) {
+  if (!g_global_boundary) {
+    g_global_boundary = true;
+    m_local_boundary = true;
+  }
+}
+
+Recorder::~Recorder() {
+  assert(m_result_recorded && "Did you forget SB_RECORD_RESULT?");
+  UpdateBoundary();
+}
+
+bool lldb_private::repro::Recorder::g_global_boundary;
Index: source/API/SBReproducerPrivate.h
===================================================================
--- /dev/null
+++ source/API/SBReproducerPrivate.h
@@ -0,0 +1,151 @@
+//===-- 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 "lldb/Utility/ReproducerInstrumentation.h"
+
+#include "llvm/ADT/DenseMap.h"
+
+namespace lldb_private {
+namespace repro {
+
+class SBRegistry : public Registry {
+public:
+  SBRegistry();
+};
+
+struct SBInfo {
+  static const char *name;
+  static const char *file;
+};
+
+class SBProvider : public Provider<SBProvider> {
+public:
+  typedef SBInfo info;
+
+  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) {}
+
+  Serializer &GetSerializer() { return m_serializer; }
+  Registry &GetRegistry() { return m_registry; }
+
+  static char ID;
+
+private:
+  std::error_code m_ec;
+  llvm::raw_fd_ostream m_stream;
+  Serializer m_serializer;
+  SBRegistry m_registry;
+};
+
+} // namespace repro
+} // namespace lldb_private
+
+#define SB_RECORD_CONSTRUCTOR(Class, Signature, ...)                           \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    lldb_private::repro::Recorder sb_recorder(                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer(),                   \
+        g->GetOrCreate<repro::SBProvider>().GetRegistry(),                     \
+        LLVM_PRETTY_FUNCTION);                                                 \
+    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::Recorder sb_recorder(                                 \
+        g->GetOrCreate<repro::SBProvider>().GetSerializer(),                   \
+        g->GetOrCreate<repro::SBProvider>().GetRegistry(),                     \
+        LLVM_PRETTY_FUNCTION);                                                 \
+    sb_recorder.Record(&lldb_private::repro::construct<Class()>::doit);        \
+    sb_recorder.RecordResult(this);                                            \
+  }
+
+#define SB_RECORD_METHOD(Result, Class, Method, Signature, ...)                \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    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, ...)          \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    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)                        \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    sb_recorder->Record(&lldb_private::repro::invoke<Result (                  \
+                            Class::*)()>::method<&Class::Method>::doit,        \
+                        this);                                                 \
+  }
+
+#define SB_RECORD_METHOD_CONST_NO_ARGS(Result, Class, Method)                  \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    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, ...)         \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    sb_recorder->Record(static_cast<Result(*) Signature>(&Class::Method),      \
+                        __VA_ARGS__);                                          \
+  }
+
+#define SB_RECORD_STATIC_METHOD_NO_ARGS(Result, Class, Method)                 \
+  llvm::Optional<lldb_private::repro::Recorder> sb_recorder;                   \
+  if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {  \
+    sb_recorder.emplace(g->GetOrCreate<repro::SBProvider>().GetSerializer(),   \
+                        g->GetOrCreate<repro::SBProvider>().GetRegistry(),     \
+                        LLVM_PRETTY_FUNCTION);                                 \
+    sb_recorder->Record(static_cast<Result (*)()>(&Class::Method));            \
+  }
+
+#define SB_RECORD_RESULT(Result)                                               \
+  sb_recorder ? sb_recorder->RecordResult(Result) : Result;
+
+#endif
Index: source/API/SBReproducer.cpp
===================================================================
--- /dev/null
+++ source/API/SBReproducer.cpp
@@ -0,0 +1,61 @@
+//===-- 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/SBAddress.h"
+#include "lldb/API/SBAttachInfo.h"
+#include "lldb/API/SBBlock.h"
+#include "lldb/API/SBBreakpoint.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBData.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API/SBDeclaration.h"
+#include "lldb/API/SBError.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 SB_REGISTER_CONSTRUCTOR(Class, Signature)                              \
+  Register<Class * Signature>(&construct<Class Signature>::doit)
+#define SB_REGISTER_METHOD(Result, Class, Method, Signature)                   \
+  Register(&invoke<Result(Class::*) Signature>::method<&Class::Method>::doit)
+#define SB_REGISTER_METHOD_CONST(Result, Class, Method, Signature)             \
+  Register(&invoke<Result(Class::*)                                            \
+                       Signature const>::method_const<&Class::Method>::doit)
+#define SB_REGISTER_STATIC_METHOD(Result, Class, Method, Signature)            \
+  Register<Result Signature>(static_cast<Result(*) Signature>(&Class::Method))
+
+SBRegistry::SBRegistry() {}
+
+bool SBReproducer::Replay() {
+  repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
+  if (!loader)
+    return false;
+
+  FileSpec file = loader->GetFile<SBInfo>();
+  if (!file)
+    return false;
+
+  SBRegistry registry;
+  registry.Replay(file);
+
+  return true;
+}
+
+char lldb_private::repro::SBProvider::ID = 0;
+const char *SBInfo::name = "sbapi";
+const char *SBInfo::file = "sbapi.bin";
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/ReproducerInstrumentation.h
===================================================================
--- include/lldb/Utility/ReproducerInstrumentation.h
+++ include/lldb/Utility/ReproducerInstrumentation.h
@@ -1,5 +1,4 @@
 //===-- ReproducerInstrumentation.h -----------------------------*- C++ -*-===//
-//
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
@@ -18,7 +17,6 @@
 #include "llvm/Support/ErrorHandling.h"
 
 #include <map>
-#include <mutex>
 
 namespace lldb_private {
 namespace repro {
@@ -222,6 +220,114 @@
   };
 };
 
+/// The replayer interface.
+struct Replayer {
+  virtual ~Replayer() {}
+  virtual void operator()(Deserializer &deserializer) const = 0;
+};
+
+/// The default replayer deserializes the arguments and calls the function.
+template <typename Signature> struct DefaultReplayer;
+template <typename Result, typename... Args>
+struct DefaultReplayer<Result(Args...)> : public Replayer {
+  DefaultReplayer(Result (*f)(Args...)) : Replayer(), f(f) {}
+
+  void operator()(Deserializer &deserializer) const override {
+    deserializer.HandleReplayResult(
+        DeserializationHelper<Args...>::template deserialized<Result>::doit(
+            deserializer, f));
+  }
+
+  Result (*f)(Args...);
+};
+
+/// Partial specialization for function returning a void type. It ignores the
+/// (absent) return value.
+template <typename... Args>
+struct DefaultReplayer<void(Args...)> : public Replayer {
+  DefaultReplayer(void (*f)(Args...)) : Replayer(), f(f) {}
+
+  void operator()(Deserializer &deserializer) const override {
+    DeserializationHelper<Args...>::template deserialized<void>::doit(
+        deserializer, f);
+    deserializer.HandleReplayResultVoid();
+  }
+
+  void (*f)(Args...);
+};
+
+/// The registry contains a unique mapping between functions and their ID. The
+/// IDs can be serialized and deserialized to replay a function. Functions need
+/// to be registered with the registry for this to work.
+class Registry {
+public:
+  Registry() = default;
+  virtual ~Registry() = default;
+
+  /// Register a default replayer for a function.
+  template <typename Signature> void Register(Signature *f) {
+    DoRegister(uintptr_t(f), llvm::make_unique<DefaultReplayer<Signature>>(f));
+  }
+
+  /// Register a replayer that invokes a custom function with the same
+  /// signature as the replayed function.
+  template <typename Signature> void Register(Signature *f, Signature *g) {
+    DoRegister(uintptr_t(f), llvm::make_unique<DefaultReplayer<Signature>>(g));
+  }
+
+  /// Replay functions from a file.
+  bool Replay(const FileSpec &file);
+
+  /// Replay functions from a buffer.
+  bool Replay(llvm::StringRef buffer);
+
+  /// Returns the ID for a given function address.
+  unsigned GetID(uintptr_t addr);
+
+protected:
+  /// Register the given replayer for a function (and the ID mapping).
+  void DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer);
+
+private:
+  /// Mapping of function addresses to replayers and their ID.
+  std::map<uintptr_t, std::pair<std::unique_ptr<Replayer>, unsigned>>
+      m_replayers;
+
+  /// Mapping of IDs to replayer instances.
+  std::map<unsigned, Replayer *> m_ids;
+};
+
+/// To be used as the "Runtime ID" of a constructor. It also invokes the
+/// constructor when called.
+template <typename Signature> struct construct;
+template <typename Class, typename... Args> struct construct<Class(Args...)> {
+  static Class *doit(Args... args) { return new Class(args...); }
+};
+
+/// To be used as the "Runtime ID" of a member function. It also invokes the
+/// member function when called.
+template <typename Signature> struct invoke;
+template <typename Result, typename Class, typename... Args>
+struct invoke<Result (Class::*)(Args...)> {
+  template <Result (Class::*m)(Args...)> struct method {
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
+  };
+};
+
+template <typename Result, typename Class, typename... Args>
+struct invoke<Result (Class::*)(Args...) const> {
+  template <Result (Class::*m)(Args...) const> struct method_const {
+    static Result doit(Class *c, Args... args) { return (c->*m)(args...); }
+  };
+};
+
+template <typename Class, typename... Args>
+struct invoke<void (Class::*)(Args...)> {
+  template <void (Class::*m)(Args...)> struct method {
+    static void doit(Class *c, Args... args) { (c->*m)(args...); }
+  };
+};
+
 /// Maps an object to an index for serialization. Indices are unique and
 /// incremented for every new object.
 ///
@@ -236,14 +342,13 @@
 private:
   unsigned GetIndexForObjectImpl(void *object);
 
-  std::mutex m_mutex;
   llvm::DenseMap<void *, unsigned> m_mapping;
 };
 
 /// Serializes functions, their arguments and their return type to a stream.
 class Serializer {
 public:
-  Serializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(stream) {}
+  Serializer(llvm::raw_ostream &stream = llvm::outs()) : m_stream(&stream) {}
 
   /// Recursively serialize all the given arguments.
   template <typename Head, typename... Tail>
@@ -255,6 +360,7 @@
   void SerializeAll() {}
 
 private:
+  llvm::raw_ostream &os() { return *m_stream; }
   /// 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).
@@ -272,7 +378,7 @@
   /// to objects (in which case we serialize their index).
   template <typename T> void Serialize(T &t) {
     if (std::is_fundamental<T>::value) {
-      m_stream.write(reinterpret_cast<const char *>(&t), sizeof(T));
+      os().write(reinterpret_cast<const char *>(&t), sizeof(T));
     } else {
       unsigned idx = m_tracker.GetIndexForObject(&t);
       Serialize(idx);
@@ -285,17 +391,113 @@
   }
 
   void Serialize(const char *t) {
-    m_stream << t;
-    m_stream.write(0x0);
+    os() << t;
+    os().write(0x0);
   }
 
   /// Serialization stream.
-  llvm::raw_ostream &m_stream;
+  llvm::raw_ostream *m_stream;
 
   /// Mapping of objects to indices.
   ObjectToIndex m_tracker;
 };
 
+/// 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 Recorder {
+public:
+  Recorder(Serializer &serializer, Registry &registry,
+           llvm::StringRef pretty_func = {});
+  ~Recorder();
+
+  /// 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 = m_registry.GetID(uintptr_t(f));
+
+    LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "#{0} '{1}'", id,
+             m_pretty_func);
+
+    m_serializer.SerializeAll(id);
+    m_serializer.SerializeAll(args...);
+
+    if (std::is_class<typename std::remove_pointer<
+            typename std::remove_reference<Result>::type>::type>::value) {
+      m_result_recorded = false;
+    } else {
+      m_serializer.SerializeAll(0);
+      m_result_recorded = true;
+    }
+  }
+
+  /// Records a single function call.
+  template <typename... Args>
+  void Record(void (*f)(Args...), const Args &... args) {
+    if (!ShouldCapture())
+      return;
+
+    unsigned id = m_registry.GetID(uintptr_t(f));
+
+    LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "#{0} '{1}'", id,
+             m_pretty_func);
+
+    m_serializer.SerializeAll(id);
+    m_serializer.SerializeAll(args...);
+
+    // Record result.
+    m_serializer.SerializeAll(0);
+    m_result_recorded = true;
+  }
+
+  /// Record the result of a function call.
+  template <typename Result> Result RecordResult(const Result &r) {
+    UpdateBoundary();
+    if (ShouldCapture()) {
+      assert(!m_result_recorded);
+      m_serializer.SerializeAll(r);
+      m_result_recorded = true;
+    }
+    return r;
+  }
+
+private:
+  void UpdateBoundary() {
+    if (m_local_boundary)
+      g_global_boundary = false;
+  }
+
+  bool ShouldCapture() { return 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.
+  Serializer &m_serializer;
+
+  // The registry with a mapping between function addresses and indices.
+  Registry &m_registry;
+
+  /// Whether this function call was the one crossing the API boundary.
+  bool m_local_boundary;
+
+  /// Whether the return value was recorded explicitly.
+  bool m_result_recorded;
+
+  /// Whether we're currently across the API boundary.
+  static bool g_global_boundary;
+};
+
 } // namespace repro
 } // namespace lldb_private
 
Index: include/lldb/API/SBReproducer.h
===================================================================
--- /dev/null
+++ include/lldb/API/SBReproducer.h
@@ -0,0 +1,23 @@
+//===-- SBReproducer.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_API_SBREPRODUCER_H
+#define LLDB_API_SBREPRODUCER_H
+
+#include "lldb/lldb-defines.h"
+
+namespace lldb {
+
+class LLDB_API SBReproducer {
+public:
+  static bool Replay();
+};
+
+} // namespace lldb
+
+#endif
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to