Skip to content

Commit

Permalink
[lldb/Reproducers] Support new replay mode: passive replay
Browse files Browse the repository at this point in the history
Support passive replay as proposed in the RFC [1] on lldb-dev and
described in more detail on the lldb website [2].

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

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

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

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

Differential revision: https://reviews.llvm.org/D77602
  • Loading branch information
JDevlieghere committed Apr 20, 2020
1 parent b3f5472 commit 950a8aa
Show file tree
Hide file tree
Showing 8 changed files with 739 additions and 136 deletions.
8 changes: 6 additions & 2 deletions lldb/include/lldb/Utility/Reproducer.h
Expand Up @@ -27,6 +27,7 @@ class Reproducer;
enum class ReproducerMode {
Capture,
Replay,
PassiveReplay,
Off,
};

Expand Down Expand Up @@ -287,7 +288,7 @@ class Generator final {

class Loader final {
public:
Loader(FileSpec root);
Loader(FileSpec root, bool passive = false);

template <typename T> FileSpec GetFile() {
if (!HasFile(T::file))
Expand All @@ -309,12 +310,15 @@ class Loader final {

const FileSpec &GetRoot() const { return m_root; }

bool IsPassiveReplay() const { return m_passive_replay; }

private:
bool HasFile(llvm::StringRef file);

FileSpec m_root;
std::vector<std::string> m_files;
bool m_loaded;
bool m_passive_replay;
};

/// The reproducer enables clients to obtain access to the Generator and
Expand Down Expand Up @@ -342,7 +346,7 @@ class Reproducer {

protected:
llvm::Error SetCapture(llvm::Optional<FileSpec> root);
llvm::Error SetReplay(llvm::Optional<FileSpec> root);
llvm::Error SetReplay(llvm::Optional<FileSpec> root, bool passive = false);

private:
static llvm::Optional<Reproducer> &InstanceImpl();
Expand Down
335 changes: 240 additions & 95 deletions lldb/include/lldb/Utility/ReproducerInstrumentation.h

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions lldb/source/API/SBDebugger.cpp
Expand Up @@ -1641,31 +1641,31 @@ static SBError SetFileRedirect(SBDebugger *, FileSP file) { return SBError(); }

template <> void RegisterMethods<SBDebugger>(Registry &R) {
// Custom implementation.
R.Register(&invoke<void (SBDebugger::*)(
FILE *, bool)>::method<&SBDebugger::SetErrorFileHandle>::doit,
R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method<
&SBDebugger::SetErrorFileHandle>::record,
&SetFileHandleRedirect);
R.Register(&invoke<void (SBDebugger::*)(
FILE *, bool)>::method<&SBDebugger::SetOutputFileHandle>::doit,
R.Register(&invoke<void (SBDebugger::*)(FILE *, bool)>::method<
&SBDebugger::SetOutputFileHandle>::record,
&SetFileHandleRedirect);

R.Register(&invoke<SBError (SBDebugger::*)(
SBFile)>::method<&SBDebugger::SetInputFile>::doit,
SBFile)>::method<&SBDebugger::SetInputFile>::record,
&SetFileRedirect);
R.Register(&invoke<SBError (SBDebugger::*)(
SBFile)>::method<&SBDebugger::SetOutputFile>::doit,
SBFile)>::method<&SBDebugger::SetOutputFile>::record,
&SetFileRedirect);
R.Register(&invoke<SBError (SBDebugger::*)(
SBFile)>::method<&SBDebugger::SetErrorFile>::doit,
SBFile)>::method<&SBDebugger::SetErrorFile>::record,
&SetFileRedirect);

R.Register(&invoke<SBError (SBDebugger::*)(
FileSP)>::method<&SBDebugger::SetInputFile>::doit,
FileSP)>::method<&SBDebugger::SetInputFile>::record,
&SetFileRedirect);
R.Register(&invoke<SBError (SBDebugger::*)(
FileSP)>::method<&SBDebugger::SetOutputFile>::doit,
FileSP)>::method<&SBDebugger::SetOutputFile>::record,
&SetFileRedirect);
R.Register(&invoke<SBError (SBDebugger::*)(
FileSP)>::method<&SBDebugger::SetErrorFile>::doit,
FileSP)>::method<&SBDebugger::SetErrorFile>::record,
&SetFileRedirect);

LLDB_REGISTER_CHAR_PTR_REDIRECT_STATIC(bool, SBDebugger,
Expand Down
28 changes: 27 additions & 1 deletion lldb/source/API/SBReproducer.cpp
Expand Up @@ -111,6 +111,12 @@ const char *SBReproducer::Capture() {
error = llvm::toString(std::move(e));
return error.c_str();
}

if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
auto &p = g->GetOrCreate<SBProvider>();
InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry());
}

return nullptr;
}

Expand All @@ -121,15 +127,35 @@ const char *SBReproducer::Capture(const char *path) {
error = llvm::toString(std::move(e));
return error.c_str();
}

if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
auto &p = g->GetOrCreate<SBProvider>();
InstrumentationData::Initialize(p.GetSerializer(), p.GetRegistry());
}

return nullptr;
}

const char *SBReproducer::PassiveReplay(const char *path) {
static std::string error;
if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
if (auto e = Reproducer::Initialize(ReproducerMode::PassiveReplay,
FileSpec(path))) {
error = llvm::toString(std::move(e));
return error.c_str();
}

if (auto *l = lldb_private::repro::Reproducer::Instance().GetLoader()) {
FileSpec file = l->GetFile<SBProvider::Info>();
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
if (!error_or_file) {
error =
"unable to read SB API data: " + error_or_file.getError().message();
return error.c_str();
}
static ReplayData r(std::move(*error_or_file));
InstrumentationData::Initialize(r.GetDeserializer(), r.GetRegistry());
}

return nullptr;
}

Expand Down
24 changes: 13 additions & 11 deletions lldb/source/API/SBReproducerPrivate.h
Expand Up @@ -20,7 +20,7 @@
#include "llvm/ADT/DenseMap.h"

#define LLDB_GET_INSTRUMENTATION_DATA() \
lldb_private::repro::GetInstrumentationData()
lldb_private::repro::InstrumentationData::Instance()

namespace lldb_private {
namespace repro {
Expand Down Expand Up @@ -55,17 +55,19 @@ class SBProvider : public Provider<SBProvider> {
SBRegistry m_registry;
};

inline InstrumentationData GetInstrumentationData() {
if (!lldb_private::repro::Reproducer::Initialized())
return {};

if (auto *g = lldb_private::repro::Reproducer::Instance().GetGenerator()) {
auto &p = g->GetOrCreate<SBProvider>();
return {p.GetSerializer(), p.GetRegistry()};
}
class ReplayData {
public:
ReplayData(std::unique_ptr<llvm::MemoryBuffer> memory_buffer)
: m_memory_buffer(std::move(memory_buffer)), m_registry(),
m_deserializer(m_memory_buffer->getBuffer()) {}
Deserializer &GetDeserializer() { return m_deserializer; }
Registry &GetRegistry() { return m_registry; }

return {};
}
private:
std::unique_ptr<llvm::MemoryBuffer> m_memory_buffer;
SBRegistry m_registry;
Deserializer m_deserializer;
};

template <typename T> void RegisterMethods(Registry &R);

Expand Down
13 changes: 8 additions & 5 deletions lldb/source/Utility/Reproducer.cpp
Expand Up @@ -62,7 +62,9 @@ llvm::Error Reproducer::Initialize(ReproducerMode mode,
return Instance().SetCapture(root);
} break;
case ReproducerMode::Replay:
return Instance().SetReplay(root);
return Instance().SetReplay(root, /*passive*/ false);
case ReproducerMode::PassiveReplay:
return Instance().SetReplay(root, /*passive*/ true);
case ReproducerMode::Off:
break;
};
Expand Down Expand Up @@ -127,7 +129,7 @@ llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
return Error::success();
}

llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) {
std::lock_guard<std::mutex> guard(m_mutex);

if (root && m_generator)
Expand All @@ -140,7 +142,7 @@ llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
return Error::success();
}

m_loader.emplace(*root);
m_loader.emplace(*root, passive);
if (auto e = m_loader->LoadIndex())
return e;

Expand Down Expand Up @@ -227,8 +229,9 @@ void Generator::AddProvidersToIndex() {
yout << files;
}

Loader::Loader(FileSpec root)
: m_root(MakeAbsolute(std::move(root))), m_loaded(false) {}
Loader::Loader(FileSpec root, bool passive)
: m_root(MakeAbsolute(std::move(root))), m_loaded(false),
m_passive_replay(passive) {}

llvm::Error Loader::LoadIndex() {
if (m_loaded)
Expand Down
41 changes: 39 additions & 2 deletions lldb/source/Utility/ReproducerInstrumentation.cpp
Expand Up @@ -8,9 +8,9 @@

#include "lldb/Utility/ReproducerInstrumentation.h"
#include "lldb/Utility/Reproducer.h"
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <thread>

using namespace lldb_private;
using namespace lldb_private::repro;
Expand Down Expand Up @@ -120,7 +120,7 @@ bool Registry::Replay(Deserializer &deserializer) {

// Add a small artificial delay to ensure that all asynchronous events have
// completed before we exit.
std::this_thread::sleep_for (std::chrono::milliseconds(100));
std::this_thread::sleep_for(std::chrono::milliseconds(100));

return true;
}
Expand All @@ -145,6 +145,22 @@ std::string Registry::GetSignature(unsigned id) {
return m_ids[id].second.ToString();
}

void Registry::CheckID(unsigned expected, unsigned actual) {
if (expected != actual) {
llvm::errs() << "Reproducer expected signature " << expected << ": '"
<< GetSignature(expected) << "'\n";
llvm::errs() << "Reproducer actual signature " << actual << ": '"
<< GetSignature(actual) << "'\n";
llvm::report_fatal_error(
"Detected reproducer replay divergence. Refusing to continue.");
}

#ifdef LLDB_REPRO_INSTR_TRACE
llvm::errs() << "Replaying " << actual << ": " << GetSignature(actual)
<< "\n";
#endif
}

Replayer *Registry::GetReplayer(unsigned id) {
assert(m_ids.count(id) != 0 && "ID not in registry");
return m_ids[id].first;
Expand Down Expand Up @@ -181,4 +197,25 @@ Recorder::~Recorder() {
UpdateBoundary();
}

void InstrumentationData::Initialize(Serializer &serializer,
Registry &registry) {
InstanceImpl().emplace(serializer, registry);
}

void InstrumentationData::Initialize(Deserializer &deserializer,
Registry &registry) {
InstanceImpl().emplace(deserializer, registry);
}

InstrumentationData &InstrumentationData::Instance() {
if (!InstanceImpl())
InstanceImpl().emplace();
return *InstanceImpl();
}

llvm::Optional<InstrumentationData> &InstrumentationData::InstanceImpl() {
static llvm::Optional<InstrumentationData> g_instrumentation_data;
return g_instrumentation_data;
}

bool lldb_private::repro::Recorder::g_global_boundary;

0 comments on commit 950a8aa

Please sign in to comment.