JDevlieghere updated this revision to Diff 185207.
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 ®istry,
+ 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 ®istry,
+ 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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits