summaryrefslogtreecommitdiff
path: root/lldb/source/Utility/ReproducerInstrumentation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Utility/ReproducerInstrumentation.cpp')
-rw-r--r--lldb/source/Utility/ReproducerInstrumentation.cpp118
1 files changed, 113 insertions, 5 deletions
diff --git a/lldb/source/Utility/ReproducerInstrumentation.cpp b/lldb/source/Utility/ReproducerInstrumentation.cpp
index 473786ef4d3e..09aea69d8313 100644
--- a/lldb/source/Utility/ReproducerInstrumentation.cpp
+++ b/lldb/source/Utility/ReproducerInstrumentation.cpp
@@ -1,4 +1,4 @@
-//===-- ReproducerInstrumentation.cpp ---------------------------*- C++ -*-===//
+//===-- ReproducerInstrumentation.cpp -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,6 +8,9 @@
#include "lldb/Utility/ReproducerInstrumentation.h"
#include "lldb/Utility/Reproducer.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <thread>
using namespace lldb_private;
using namespace lldb_private::repro;
@@ -21,19 +24,66 @@ void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
m_mapping[idx] = object;
}
+std::vector<void *> IndexToObject::GetAllObjects() const {
+ std::vector<std::pair<unsigned, void *>> pairs;
+ for (auto &e : m_mapping) {
+ pairs.emplace_back(e.first, e.second);
+ }
+
+ // Sort based on index.
+ std::sort(pairs.begin(), pairs.end(),
+ [](auto &lhs, auto &rhs) { return lhs.first < rhs.first; });
+
+ std::vector<void *> objects;
+ objects.reserve(pairs.size());
+ for (auto &p : pairs) {
+ objects.push_back(p.second);
+ }
+
+ return objects;
+}
+
+template <> const uint8_t *Deserializer::Deserialize<const uint8_t *>() {
+ return Deserialize<uint8_t *>();
+}
+
+template <> void *Deserializer::Deserialize<void *>() {
+ return const_cast<void *>(Deserialize<const void *>());
+}
+
+template <> const void *Deserializer::Deserialize<const void *>() {
+ return nullptr;
+}
+
template <> char *Deserializer::Deserialize<char *>() {
return const_cast<char *>(Deserialize<const char *>());
}
template <> const char *Deserializer::Deserialize<const char *>() {
- auto pos = m_buffer.find('\0');
- if (pos == llvm::StringRef::npos)
+ const size_t size = Deserialize<size_t>();
+ if (size == std::numeric_limits<size_t>::max())
return nullptr;
+ assert(HasData(size + 1));
const char *str = m_buffer.data();
- m_buffer = m_buffer.drop_front(pos + 1);
+ m_buffer = m_buffer.drop_front(size + 1);
+#ifdef LLDB_REPRO_INSTR_TRACE
+ llvm::errs() << "Deserializing with " << LLVM_PRETTY_FUNCTION << " -> \""
+ << str << "\"\n";
+#endif
return str;
}
+template <> const char **Deserializer::Deserialize<const char **>() {
+ const size_t size = Deserialize<size_t>();
+ if (size == 0)
+ return nullptr;
+ const char **r =
+ reinterpret_cast<const char **>(calloc(size + 1, sizeof(char *)));
+ for (size_t i = 0; i < size; ++i)
+ r[i] = Deserialize<const char *>();
+ return r;
+}
+
bool Registry::Replay(const FileSpec &file) {
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
if (auto err = error_or_file.getError())
@@ -43,11 +93,19 @@ bool Registry::Replay(const FileSpec &file) {
}
bool Registry::Replay(llvm::StringRef buffer) {
+ Deserializer deserializer(buffer);
+ return Replay(deserializer);
+}
+
+bool Registry::Replay(Deserializer &deserializer) {
#ifndef LLDB_REPRO_INSTR_TRACE
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_API);
#endif
- Deserializer deserializer(buffer);
+ // Disable buffering stdout so that we approximate the way things get flushed
+ // during an interactive session.
+ setvbuf(stdout, nullptr, _IONBF, 0);
+
while (deserializer.HasData(1)) {
unsigned id = deserializer.Deserialize<unsigned>();
@@ -60,6 +118,10 @@ bool Registry::Replay(llvm::StringRef buffer) {
GetReplayer(id)->operator()(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));
+
return true;
}
@@ -83,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;
@@ -101,6 +179,15 @@ unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
return m_mapping[object];
}
+Recorder::Recorder()
+ : m_serializer(nullptr), m_pretty_func(), m_pretty_args(),
+ m_local_boundary(false), m_result_recorded(true) {
+ if (!g_global_boundary) {
+ g_global_boundary = true;
+ m_local_boundary = true;
+ }
+}
+
Recorder::Recorder(llvm::StringRef pretty_func, std::string &&pretty_args)
: m_serializer(nullptr), m_pretty_func(pretty_func),
m_pretty_args(pretty_args), m_local_boundary(false),
@@ -119,4 +206,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;