You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
6.9 KiB
231 lines
6.9 KiB
//===-- 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.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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;
|
|
|
|
void *IndexToObject::GetObjectForIndexImpl(unsigned idx) {
|
|
return m_mapping.lookup(idx);
|
|
}
|
|
|
|
void IndexToObject::AddObjectForIndexImpl(unsigned idx, void *object) {
|
|
assert(idx != 0 && "Cannot add object for sentinel");
|
|
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 *>() {
|
|
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(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())
|
|
return false;
|
|
|
|
return Replay((*error_or_file)->getBuffer());
|
|
}
|
|
|
|
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
|
|
|
|
// 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>();
|
|
|
|
#ifndef LLDB_REPRO_INSTR_TRACE
|
|
LLDB_LOG(log, "Replaying {0}: {1}", id, GetSignature(id));
|
|
#else
|
|
llvm::errs() << "Replaying " << id << ": " << GetSignature(id) << "\n";
|
|
#endif
|
|
|
|
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;
|
|
}
|
|
|
|
void Registry::DoRegister(uintptr_t RunID, std::unique_ptr<Replayer> replayer,
|
|
SignatureStr signature) {
|
|
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] =
|
|
std::make_pair(m_replayers[RunID].first.get(), std::move(signature));
|
|
}
|
|
|
|
unsigned Registry::GetID(uintptr_t addr) {
|
|
unsigned id = m_replayers[addr].second;
|
|
assert(id != 0 && "Forgot to add function to registry?");
|
|
return id;
|
|
}
|
|
|
|
std::string Registry::GetSignature(unsigned id) {
|
|
assert(m_ids.count(id) != 0 && "ID not in registry");
|
|
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;
|
|
}
|
|
|
|
std::string Registry::SignatureStr::ToString() const {
|
|
return (result + (result.empty() ? "" : " ") + scope + "::" + name + args)
|
|
.str();
|
|
}
|
|
|
|
unsigned ObjectToIndex::GetIndexForObjectImpl(const void *object) {
|
|
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()
|
|
: 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),
|
|
m_result_recorded(true) {
|
|
if (!g_global_boundary) {
|
|
g_global_boundary = true;
|
|
m_local_boundary = true;
|
|
|
|
LLDB_LOG(GetLogIfAllCategoriesSet(LIBLLDB_LOG_API), "{0} ({1})",
|
|
m_pretty_func, m_pretty_args);
|
|
}
|
|
}
|
|
|
|
Recorder::~Recorder() {
|
|
assert(m_result_recorded && "Did you forget LLDB_RECORD_RESULT?");
|
|
UpdateBoundary();
|
|
}
|
|
|
|
void InstrumentationData::Initialize(Serializer &serializer,
|
|
Registry ®istry) {
|
|
InstanceImpl().emplace(serializer, registry);
|
|
}
|
|
|
|
void InstrumentationData::Initialize(Deserializer &deserializer,
|
|
Registry ®istry) {
|
|
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;
|
|
}
|
|
|
|
thread_local bool lldb_private::repro::Recorder::g_global_boundary = false;
|