forked from OSchip/llvm-project
[lldb] Extract reproducer providers & co into their own header.
Extract all the provider related logic from Reproducer.h and move it into its own header ReproducerProvider.h. These classes are seeing most of the development these days and this reorganization reduces incremental compilation from ~520 to ~110 files when making changes to the new header.
This commit is contained in:
parent
8e06bf6b3a
commit
bb894b9782
|
@ -15,7 +15,6 @@
|
||||||
#include "lldb/Utility/ConstString.h"
|
#include "lldb/Utility/ConstString.h"
|
||||||
#include "lldb/Utility/Flags.h"
|
#include "lldb/Utility/Flags.h"
|
||||||
#include "lldb/Utility/Predicate.h"
|
#include "lldb/Utility/Predicate.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
|
||||||
#include "lldb/Utility/Stream.h"
|
#include "lldb/Utility/Stream.h"
|
||||||
#include "lldb/Utility/StringList.h"
|
#include "lldb/Utility/StringList.h"
|
||||||
#include "lldb/lldb-defines.h"
|
#include "lldb/lldb-defines.h"
|
||||||
|
@ -32,6 +31,9 @@
|
||||||
|
|
||||||
namespace lldb_private {
|
namespace lldb_private {
|
||||||
class Debugger;
|
class Debugger;
|
||||||
|
namespace repro {
|
||||||
|
class DataRecorder;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace curses {
|
namespace curses {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#define LLDB_UTILITY_GDBREMOTE_H
|
#define LLDB_UTILITY_GDBREMOTE_H
|
||||||
|
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/lldb-enumerations.h"
|
#include "lldb/lldb-enumerations.h"
|
||||||
#include "lldb/lldb-public.h"
|
#include "lldb/lldb-public.h"
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "lldb/Utility/Environment.h"
|
#include "lldb/Utility/Environment.h"
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/NameMatches.h"
|
#include "lldb/Utility/NameMatches.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
|
||||||
#include "llvm/Support/YAMLTraits.h"
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -217,40 +216,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace repro {
|
namespace repro {
|
||||||
class ProcessInfoRecorder : public AbstractRecorder {
|
|
||||||
public:
|
|
||||||
ProcessInfoRecorder(const FileSpec &filename, std::error_code &ec)
|
|
||||||
: AbstractRecorder(filename, ec) {}
|
|
||||||
|
|
||||||
static llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
|
|
||||||
Create(const FileSpec &filename);
|
|
||||||
|
|
||||||
void Record(const ProcessInstanceInfoList &process_infos);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
|
|
||||||
public:
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
ProcessInfoProvider(const FileSpec &directory) : Provider(directory) {}
|
|
||||||
|
|
||||||
ProcessInfoRecorder *GetNewProcessInfoRecorder();
|
|
||||||
|
|
||||||
void Keep() override;
|
|
||||||
void Discard() override;
|
|
||||||
|
|
||||||
static char ID;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
|
|
||||||
std::vector<std::unique_ptr<ProcessInfoRecorder>> m_process_info_recorders;
|
|
||||||
};
|
|
||||||
|
|
||||||
llvm::Optional<ProcessInstanceInfoList> GetReplayProcessInstanceInfoList();
|
llvm::Optional<ProcessInstanceInfoList> GetReplayProcessInstanceInfoList();
|
||||||
|
|
||||||
} // namespace repro
|
} // namespace repro
|
||||||
} // namespace lldb_private
|
} // namespace lldb_private
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
#include "llvm/Support/FileCollector.h"
|
#include "llvm/Support/VirtualFileSystem.h"
|
||||||
#include "llvm/Support/YAMLTraits.h"
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -84,250 +84,6 @@ protected:
|
||||||
using ProviderBase::ProviderBase; // Inherit constructor.
|
using ProviderBase::ProviderBase; // Inherit constructor.
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileProvider : public Provider<FileProvider> {
|
|
||||||
public:
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
FileProvider(const FileSpec &directory)
|
|
||||||
: Provider(directory),
|
|
||||||
m_collector(std::make_shared<llvm::FileCollector>(
|
|
||||||
directory.CopyByAppendingPathComponent("root").GetPath(),
|
|
||||||
directory.GetPath())) {}
|
|
||||||
|
|
||||||
std::shared_ptr<llvm::FileCollector> GetFileCollector() {
|
|
||||||
return m_collector;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RecordInterestingDirectory(const llvm::Twine &dir);
|
|
||||||
void RecordInterestingDirectoryRecursive(const llvm::Twine &dir);
|
|
||||||
|
|
||||||
void Keep() override {
|
|
||||||
auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
|
|
||||||
// Temporary files that are removed during execution can cause copy errors.
|
|
||||||
if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
|
|
||||||
return;
|
|
||||||
m_collector->writeMapping(mapping.GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
static char ID;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<llvm::FileCollector> m_collector;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provider for the LLDB version number.
|
|
||||||
///
|
|
||||||
/// When the reproducer is kept, it writes the lldb version to a file named
|
|
||||||
/// version.txt in the reproducer root.
|
|
||||||
class VersionProvider : public Provider<VersionProvider> {
|
|
||||||
public:
|
|
||||||
VersionProvider(const FileSpec &directory) : Provider(directory) {}
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
void SetVersion(std::string version) {
|
|
||||||
assert(m_version.empty());
|
|
||||||
m_version = std::move(version);
|
|
||||||
}
|
|
||||||
void Keep() override;
|
|
||||||
std::string m_version;
|
|
||||||
static char ID;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Abstract provider to storing directory paths.
|
|
||||||
template <typename T> class DirectoryProvider : public repro::Provider<T> {
|
|
||||||
public:
|
|
||||||
DirectoryProvider(const FileSpec &root) : Provider<T>(root) {}
|
|
||||||
void SetDirectory(std::string directory) {
|
|
||||||
m_directory = std::move(directory);
|
|
||||||
}
|
|
||||||
llvm::StringRef GetDirectory() { return m_directory; }
|
|
||||||
|
|
||||||
void Keep() override {
|
|
||||||
FileSpec file = this->GetRoot().CopyByAppendingPathComponent(T::Info::file);
|
|
||||||
std::error_code ec;
|
|
||||||
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
|
||||||
if (ec)
|
|
||||||
return;
|
|
||||||
os << m_directory << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
std::string m_directory;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provider for the current working directory.
|
|
||||||
///
|
|
||||||
/// When the reproducer is kept, it writes lldb's current working directory to
|
|
||||||
/// a file named cwd.txt in the reproducer root.
|
|
||||||
class WorkingDirectoryProvider
|
|
||||||
: public DirectoryProvider<WorkingDirectoryProvider> {
|
|
||||||
public:
|
|
||||||
WorkingDirectoryProvider(const FileSpec &directory)
|
|
||||||
: DirectoryProvider(directory) {
|
|
||||||
llvm::SmallString<128> cwd;
|
|
||||||
if (std::error_code EC = llvm::sys::fs::current_path(cwd))
|
|
||||||
return;
|
|
||||||
SetDirectory(std::string(cwd));
|
|
||||||
}
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
static char ID;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Provider for the home directory.
|
|
||||||
///
|
|
||||||
/// When the reproducer is kept, it writes the user's home directory to a file
|
|
||||||
/// a file named home.txt in the reproducer root.
|
|
||||||
class HomeDirectoryProvider : public DirectoryProvider<HomeDirectoryProvider> {
|
|
||||||
public:
|
|
||||||
HomeDirectoryProvider(const FileSpec &directory)
|
|
||||||
: DirectoryProvider(directory) {
|
|
||||||
llvm::SmallString<128> home_dir;
|
|
||||||
llvm::sys::path::home_directory(home_dir);
|
|
||||||
SetDirectory(std::string(home_dir));
|
|
||||||
}
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
static char ID;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The recorder is a small object handed out by a provider to record data. It
|
|
||||||
/// is commonly used in combination with a MultiProvider which is meant to
|
|
||||||
/// record information for multiple instances of the same source of data.
|
|
||||||
class AbstractRecorder {
|
|
||||||
protected:
|
|
||||||
AbstractRecorder(const FileSpec &filename, std::error_code &ec)
|
|
||||||
: m_filename(filename.GetFilename().GetStringRef()),
|
|
||||||
m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
const FileSpec &GetFilename() { return m_filename; }
|
|
||||||
|
|
||||||
void Stop() {
|
|
||||||
assert(m_record);
|
|
||||||
m_record = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FileSpec m_filename;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
llvm::raw_fd_ostream m_os;
|
|
||||||
bool m_record;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Recorder that records its data as text to a file.
|
|
||||||
class DataRecorder : public AbstractRecorder {
|
|
||||||
public:
|
|
||||||
DataRecorder(const FileSpec &filename, std::error_code &ec)
|
|
||||||
: AbstractRecorder(filename, ec) {}
|
|
||||||
|
|
||||||
static llvm::Expected<std::unique_ptr<DataRecorder>>
|
|
||||||
Create(const FileSpec &filename);
|
|
||||||
|
|
||||||
template <typename T> void Record(const T &t, bool newline = false) {
|
|
||||||
if (!m_record)
|
|
||||||
return;
|
|
||||||
m_os << t;
|
|
||||||
if (newline)
|
|
||||||
m_os << '\n';
|
|
||||||
m_os.flush();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Recorder that records its data as YAML to a file.
|
|
||||||
class YamlRecorder : public AbstractRecorder {
|
|
||||||
public:
|
|
||||||
YamlRecorder(const FileSpec &filename, std::error_code &ec)
|
|
||||||
: AbstractRecorder(filename, ec) {}
|
|
||||||
|
|
||||||
static llvm::Expected<std::unique_ptr<YamlRecorder>>
|
|
||||||
Create(const FileSpec &filename);
|
|
||||||
|
|
||||||
template <typename T> void Record(const T &t) {
|
|
||||||
if (!m_record)
|
|
||||||
return;
|
|
||||||
llvm::yaml::Output yout(m_os);
|
|
||||||
// The YAML traits are defined as non-const because they are used for
|
|
||||||
// serialization and deserialization. The cast is safe because
|
|
||||||
// serialization doesn't modify the object.
|
|
||||||
yout << const_cast<T &>(t);
|
|
||||||
m_os.flush();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The MultiProvider is a provider that hands out recorder which can be used
|
|
||||||
/// to capture data for different instances of the same object. The recorders
|
|
||||||
/// can be passed around or stored as an instance member.
|
|
||||||
///
|
|
||||||
/// The Info::file for the MultiProvider contains an index of files for every
|
|
||||||
/// recorder. Use the MultiLoader to read the index and get the individual
|
|
||||||
/// files.
|
|
||||||
template <typename T, typename V>
|
|
||||||
class MultiProvider : public repro::Provider<V> {
|
|
||||||
public:
|
|
||||||
MultiProvider(const FileSpec &directory) : Provider<V>(directory) {}
|
|
||||||
|
|
||||||
T *GetNewRecorder() {
|
|
||||||
std::size_t i = m_recorders.size() + 1;
|
|
||||||
std::string filename = (llvm::Twine(V::Info::name) + llvm::Twine("-") +
|
|
||||||
llvm::Twine(i) + llvm::Twine(".yaml"))
|
|
||||||
.str();
|
|
||||||
auto recorder_or_error =
|
|
||||||
T::Create(this->GetRoot().CopyByAppendingPathComponent(filename));
|
|
||||||
if (!recorder_or_error) {
|
|
||||||
llvm::consumeError(recorder_or_error.takeError());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_recorders.push_back(std::move(*recorder_or_error));
|
|
||||||
return m_recorders.back().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Keep() override {
|
|
||||||
std::vector<std::string> files;
|
|
||||||
for (auto &recorder : m_recorders) {
|
|
||||||
recorder->Stop();
|
|
||||||
files.push_back(recorder->GetFilename().GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSpec file = this->GetRoot().CopyByAppendingPathComponent(V::Info::file);
|
|
||||||
std::error_code ec;
|
|
||||||
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
|
||||||
if (ec)
|
|
||||||
return;
|
|
||||||
llvm::yaml::Output yout(os);
|
|
||||||
yout << files;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Discard() override { m_recorders.clear(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::unique_ptr<T>> m_recorders;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CommandProvider : public MultiProvider<DataRecorder, CommandProvider> {
|
|
||||||
public:
|
|
||||||
struct Info {
|
|
||||||
static const char *name;
|
|
||||||
static const char *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
CommandProvider(const FileSpec &directory)
|
|
||||||
: MultiProvider<DataRecorder, CommandProvider>(directory) {}
|
|
||||||
|
|
||||||
static char ID;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The generator is responsible for the logic needed to generate a
|
/// The generator is responsible for the logic needed to generate a
|
||||||
/// reproducer. For doing so it relies on providers, who serialize data that
|
/// reproducer. For doing so it relies on providers, who serialize data that
|
||||||
/// is necessary for reproducing a failure.
|
/// is necessary for reproducing a failure.
|
||||||
|
@ -469,60 +225,6 @@ private:
|
||||||
mutable std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Loader for data captured with the MultiProvider. It will read the index and
|
|
||||||
/// return the path to the files in the index.
|
|
||||||
template <typename T> class MultiLoader {
|
|
||||||
public:
|
|
||||||
MultiLoader(std::vector<std::string> files) : m_files(std::move(files)) {}
|
|
||||||
|
|
||||||
static std::unique_ptr<MultiLoader> Create(Loader *loader) {
|
|
||||||
if (!loader)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
FileSpec file = loader->GetFile<typename T::Info>();
|
|
||||||
if (!file)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
|
|
||||||
if (auto err = error_or_file.getError())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::vector<std::string> files;
|
|
||||||
llvm::yaml::Input yin((*error_or_file)->getBuffer());
|
|
||||||
yin >> files;
|
|
||||||
|
|
||||||
if (auto err = yin.error())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
for (auto &file : files) {
|
|
||||||
FileSpec absolute_path =
|
|
||||||
loader->GetRoot().CopyByAppendingPathComponent(file);
|
|
||||||
file = absolute_path.GetPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_unique<MultiLoader<T>>(std::move(files));
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Optional<std::string> GetNextFile() {
|
|
||||||
if (m_index >= m_files.size())
|
|
||||||
return {};
|
|
||||||
return m_files[m_index++];
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<std::string> m_files;
|
|
||||||
unsigned m_index = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Helper to read directories written by the DirectoryProvider.
|
|
||||||
template <typename T>
|
|
||||||
llvm::Expected<std::string> GetDirectoryFrom(repro::Loader *loader) {
|
|
||||||
llvm::Expected<std::string> dir = loader->LoadBuffer<T>();
|
|
||||||
if (!dir)
|
|
||||||
return dir.takeError();
|
|
||||||
return std::string(llvm::StringRef(*dir).rtrim());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace repro
|
} // namespace repro
|
||||||
} // namespace lldb_private
|
} // namespace lldb_private
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
//===-- Reproducer.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_UTILITY_REPRODUCER_PROVIDER_H
|
||||||
|
#define LLDB_UTILITY_REPRODUCER_PROVIDER_H
|
||||||
|
|
||||||
|
#include "lldb/Utility/FileSpec.h"
|
||||||
|
#include "lldb/Utility/ProcessInfo.h"
|
||||||
|
#include "lldb/Utility/Reproducer.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/Support/Error.h"
|
||||||
|
#include "llvm/Support/FileCollector.h"
|
||||||
|
#include "llvm/Support/YAMLTraits.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace lldb_private {
|
||||||
|
namespace repro {
|
||||||
|
|
||||||
|
/// The recorder is a small object handed out by a provider to record data. It
|
||||||
|
/// is commonly used in combination with a MultiProvider which is meant to
|
||||||
|
/// record information for multiple instances of the same source of data.
|
||||||
|
class AbstractRecorder {
|
||||||
|
protected:
|
||||||
|
AbstractRecorder(const FileSpec &filename, std::error_code &ec)
|
||||||
|
: m_filename(filename.GetFilename().GetStringRef()),
|
||||||
|
m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const FileSpec &GetFilename() { return m_filename; }
|
||||||
|
|
||||||
|
void Stop() {
|
||||||
|
assert(m_record);
|
||||||
|
m_record = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileSpec m_filename;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
llvm::raw_fd_ostream m_os;
|
||||||
|
bool m_record;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Recorder that records its data as text to a file.
|
||||||
|
class DataRecorder : public AbstractRecorder {
|
||||||
|
public:
|
||||||
|
DataRecorder(const FileSpec &filename, std::error_code &ec)
|
||||||
|
: AbstractRecorder(filename, ec) {}
|
||||||
|
|
||||||
|
static llvm::Expected<std::unique_ptr<DataRecorder>>
|
||||||
|
Create(const FileSpec &filename);
|
||||||
|
|
||||||
|
template <typename T> void Record(const T &t, bool newline = false) {
|
||||||
|
if (!m_record)
|
||||||
|
return;
|
||||||
|
m_os << t;
|
||||||
|
if (newline)
|
||||||
|
m_os << '\n';
|
||||||
|
m_os.flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Recorder that records its data as YAML to a file.
|
||||||
|
class YamlRecorder : public AbstractRecorder {
|
||||||
|
public:
|
||||||
|
YamlRecorder(const FileSpec &filename, std::error_code &ec)
|
||||||
|
: AbstractRecorder(filename, ec) {}
|
||||||
|
|
||||||
|
static llvm::Expected<std::unique_ptr<YamlRecorder>>
|
||||||
|
Create(const FileSpec &filename);
|
||||||
|
|
||||||
|
template <typename T> void Record(const T &t) {
|
||||||
|
if (!m_record)
|
||||||
|
return;
|
||||||
|
llvm::yaml::Output yout(m_os);
|
||||||
|
// The YAML traits are defined as non-const because they are used for
|
||||||
|
// serialization and deserialization. The cast is safe because
|
||||||
|
// serialization doesn't modify the object.
|
||||||
|
yout << const_cast<T &>(t);
|
||||||
|
m_os.flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileProvider : public Provider<FileProvider> {
|
||||||
|
public:
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
FileProvider(const FileSpec &directory)
|
||||||
|
: Provider(directory),
|
||||||
|
m_collector(std::make_shared<llvm::FileCollector>(
|
||||||
|
directory.CopyByAppendingPathComponent("root").GetPath(),
|
||||||
|
directory.GetPath())) {}
|
||||||
|
|
||||||
|
std::shared_ptr<llvm::FileCollector> GetFileCollector() {
|
||||||
|
return m_collector;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordInterestingDirectory(const llvm::Twine &dir);
|
||||||
|
void RecordInterestingDirectoryRecursive(const llvm::Twine &dir);
|
||||||
|
|
||||||
|
void Keep() override {
|
||||||
|
auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
|
||||||
|
// Temporary files that are removed during execution can cause copy errors.
|
||||||
|
if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
|
||||||
|
return;
|
||||||
|
m_collector->writeMapping(mapping.GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
static char ID;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<llvm::FileCollector> m_collector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provider for the LLDB version number.
|
||||||
|
///
|
||||||
|
/// When the reproducer is kept, it writes the lldb version to a file named
|
||||||
|
/// version.txt in the reproducer root.
|
||||||
|
class VersionProvider : public Provider<VersionProvider> {
|
||||||
|
public:
|
||||||
|
VersionProvider(const FileSpec &directory) : Provider(directory) {}
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
void SetVersion(std::string version) {
|
||||||
|
assert(m_version.empty());
|
||||||
|
m_version = std::move(version);
|
||||||
|
}
|
||||||
|
void Keep() override;
|
||||||
|
std::string m_version;
|
||||||
|
static char ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Abstract provider to storing directory paths.
|
||||||
|
template <typename T> class DirectoryProvider : public repro::Provider<T> {
|
||||||
|
public:
|
||||||
|
DirectoryProvider(const FileSpec &root) : Provider<T>(root) {}
|
||||||
|
void SetDirectory(std::string directory) {
|
||||||
|
m_directory = std::move(directory);
|
||||||
|
}
|
||||||
|
llvm::StringRef GetDirectory() { return m_directory; }
|
||||||
|
|
||||||
|
void Keep() override {
|
||||||
|
FileSpec file = this->GetRoot().CopyByAppendingPathComponent(T::Info::file);
|
||||||
|
std::error_code ec;
|
||||||
|
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
||||||
|
if (ec)
|
||||||
|
return;
|
||||||
|
os << m_directory << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_directory;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provider for the current working directory.
|
||||||
|
///
|
||||||
|
/// When the reproducer is kept, it writes lldb's current working directory to
|
||||||
|
/// a file named cwd.txt in the reproducer root.
|
||||||
|
class WorkingDirectoryProvider
|
||||||
|
: public DirectoryProvider<WorkingDirectoryProvider> {
|
||||||
|
public:
|
||||||
|
WorkingDirectoryProvider(const FileSpec &directory)
|
||||||
|
: DirectoryProvider(directory) {
|
||||||
|
llvm::SmallString<128> cwd;
|
||||||
|
if (std::error_code EC = llvm::sys::fs::current_path(cwd))
|
||||||
|
return;
|
||||||
|
SetDirectory(std::string(cwd));
|
||||||
|
}
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
static char ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Provider for the home directory.
|
||||||
|
///
|
||||||
|
/// When the reproducer is kept, it writes the user's home directory to a file
|
||||||
|
/// a file named home.txt in the reproducer root.
|
||||||
|
class HomeDirectoryProvider : public DirectoryProvider<HomeDirectoryProvider> {
|
||||||
|
public:
|
||||||
|
HomeDirectoryProvider(const FileSpec &directory)
|
||||||
|
: DirectoryProvider(directory) {
|
||||||
|
llvm::SmallString<128> home_dir;
|
||||||
|
llvm::sys::path::home_directory(home_dir);
|
||||||
|
SetDirectory(std::string(home_dir));
|
||||||
|
}
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
static char ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The MultiProvider is a provider that hands out recorder which can be used
|
||||||
|
/// to capture data for different instances of the same object. The recorders
|
||||||
|
/// can be passed around or stored as an instance member.
|
||||||
|
///
|
||||||
|
/// The Info::file for the MultiProvider contains an index of files for every
|
||||||
|
/// recorder. Use the MultiLoader to read the index and get the individual
|
||||||
|
/// files.
|
||||||
|
template <typename T, typename V>
|
||||||
|
class MultiProvider : public repro::Provider<V> {
|
||||||
|
public:
|
||||||
|
MultiProvider(const FileSpec &directory) : Provider<V>(directory) {}
|
||||||
|
|
||||||
|
T *GetNewRecorder() {
|
||||||
|
std::size_t i = m_recorders.size() + 1;
|
||||||
|
std::string filename = (llvm::Twine(V::Info::name) + llvm::Twine("-") +
|
||||||
|
llvm::Twine(i) + llvm::Twine(".yaml"))
|
||||||
|
.str();
|
||||||
|
auto recorder_or_error =
|
||||||
|
T::Create(this->GetRoot().CopyByAppendingPathComponent(filename));
|
||||||
|
if (!recorder_or_error) {
|
||||||
|
llvm::consumeError(recorder_or_error.takeError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_recorders.push_back(std::move(*recorder_or_error));
|
||||||
|
return m_recorders.back().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Keep() override {
|
||||||
|
std::vector<std::string> files;
|
||||||
|
for (auto &recorder : m_recorders) {
|
||||||
|
recorder->Stop();
|
||||||
|
files.push_back(recorder->GetFilename().GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSpec file = this->GetRoot().CopyByAppendingPathComponent(V::Info::file);
|
||||||
|
std::error_code ec;
|
||||||
|
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
||||||
|
if (ec)
|
||||||
|
return;
|
||||||
|
llvm::yaml::Output yout(os);
|
||||||
|
yout << files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Discard() override { m_recorders.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::unique_ptr<T>> m_recorders;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommandProvider : public MultiProvider<DataRecorder, CommandProvider> {
|
||||||
|
public:
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
CommandProvider(const FileSpec &directory)
|
||||||
|
: MultiProvider<DataRecorder, CommandProvider>(directory) {}
|
||||||
|
|
||||||
|
static char ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProcessInfoRecorder : public AbstractRecorder {
|
||||||
|
public:
|
||||||
|
ProcessInfoRecorder(const FileSpec &filename, std::error_code &ec)
|
||||||
|
: AbstractRecorder(filename, ec) {}
|
||||||
|
|
||||||
|
static llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
|
||||||
|
Create(const FileSpec &filename);
|
||||||
|
|
||||||
|
void Record(const ProcessInstanceInfoList &process_infos);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
|
||||||
|
public:
|
||||||
|
struct Info {
|
||||||
|
static const char *name;
|
||||||
|
static const char *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
ProcessInfoProvider(const FileSpec &directory) : Provider(directory) {}
|
||||||
|
|
||||||
|
ProcessInfoRecorder *GetNewProcessInfoRecorder();
|
||||||
|
|
||||||
|
void Keep() override;
|
||||||
|
void Discard() override;
|
||||||
|
|
||||||
|
static char ID;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
|
||||||
|
std::vector<std::unique_ptr<ProcessInfoRecorder>> m_process_info_recorders;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Loader for data captured with the MultiProvider. It will read the index and
|
||||||
|
/// return the path to the files in the index.
|
||||||
|
template <typename T> class MultiLoader {
|
||||||
|
public:
|
||||||
|
MultiLoader(std::vector<std::string> files) : m_files(std::move(files)) {}
|
||||||
|
|
||||||
|
static std::unique_ptr<MultiLoader> Create(Loader *loader) {
|
||||||
|
if (!loader)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
FileSpec file = loader->GetFile<typename T::Info>();
|
||||||
|
if (!file)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
|
||||||
|
if (auto err = error_or_file.getError())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::vector<std::string> files;
|
||||||
|
llvm::yaml::Input yin((*error_or_file)->getBuffer());
|
||||||
|
yin >> files;
|
||||||
|
|
||||||
|
if (auto err = yin.error())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
for (auto &file : files) {
|
||||||
|
FileSpec absolute_path =
|
||||||
|
loader->GetRoot().CopyByAppendingPathComponent(file);
|
||||||
|
file = absolute_path.GetPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<MultiLoader<T>>(std::move(files));
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Optional<std::string> GetNextFile() {
|
||||||
|
if (m_index >= m_files.size())
|
||||||
|
return {};
|
||||||
|
return m_files[m_index++];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string> m_files;
|
||||||
|
unsigned m_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Helper to read directories written by the DirectoryProvider.
|
||||||
|
template <typename T>
|
||||||
|
llvm::Expected<std::string> GetDirectoryFrom(repro::Loader *loader) {
|
||||||
|
llvm::Expected<std::string> dir = loader->LoadBuffer<T>();
|
||||||
|
if (!dir)
|
||||||
|
return dir.takeError();
|
||||||
|
return std::string(llvm::StringRef(*dir).rtrim());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace repro
|
||||||
|
} // namespace lldb_private
|
||||||
|
|
||||||
|
#endif // LLDB_UTILITY_REPRODUCER_PROVIDER_H
|
|
@ -16,6 +16,7 @@
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/Reproducer.h"
|
||||||
#include "lldb/Utility/ReproducerInstrumentation.h"
|
#include "lldb/Utility/ReproducerInstrumentation.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
|
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "lldb/Host/Config.h"
|
#include "lldb/Host/Config.h"
|
||||||
#include "lldb/Host/File.h"
|
#include "lldb/Host/File.h"
|
||||||
#include "lldb/Utility/Predicate.h"
|
#include "lldb/Utility/Predicate.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/Status.h"
|
#include "lldb/Utility/Status.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/Utility/StringList.h"
|
#include "lldb/Utility/StringList.h"
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/Utility/Predicate.h"
|
#include "lldb/Utility/Predicate.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/Status.h"
|
#include "lldb/Utility/Status.h"
|
||||||
#include "lldb/lldb-private-forward.h"
|
#include "lldb/lldb-private-forward.h"
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
#include "lldb/Host/HostInfo.h"
|
#include "lldb/Host/HostInfo.h"
|
||||||
#include "lldb/Host/Socket.h"
|
#include "lldb/Host/Socket.h"
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/Timer.h"
|
#include "lldb/Utility/Timer.h"
|
||||||
#include "lldb/lldb-private.h"
|
#include "lldb/lldb-private.h"
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "lldb/Core/PluginManager.h"
|
#include "lldb/Core/PluginManager.h"
|
||||||
#include "lldb/Core/StreamFile.h"
|
#include "lldb/Core/StreamFile.h"
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
|
#include "lldb/Utility/Reproducer.h"
|
||||||
#include "lldb/Utility/State.h"
|
#include "lldb/Utility/State.h"
|
||||||
#include "lldb/Utility/Stream.h"
|
#include "lldb/Utility/Stream.h"
|
||||||
#include "lldb/Utility/Timer.h"
|
#include "lldb/Utility/Timer.h"
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
#include "lldb/Utility/DataBufferHeap.h"
|
#include "lldb/Utility/DataBufferHeap.h"
|
||||||
#include "lldb/Utility/LLDBAssert.h"
|
#include "lldb/Utility/LLDBAssert.h"
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/Stream.h"
|
#include "lldb/Utility/Stream.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/Utility/StringList.h"
|
#include "lldb/Utility/StringList.h"
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include "lldb/Utility/FileSpec.h"
|
#include "lldb/Utility/FileSpec.h"
|
||||||
#include "lldb/Utility/LLDBAssert.h"
|
#include "lldb/Utility/LLDBAssert.h"
|
||||||
#include "lldb/Utility/Log.h"
|
#include "lldb/Utility/Log.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#include "lldb/Symbol/LocateSymbolFile.h"
|
#include "lldb/Symbol/LocateSymbolFile.h"
|
||||||
#include "lldb/Symbol/ObjectFile.h"
|
#include "lldb/Symbol/ObjectFile.h"
|
||||||
#include "lldb/Target/Target.h"
|
#include "lldb/Target/Target.h"
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/Utility/Timer.h"
|
#include "lldb/Utility/Timer.h"
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ add_lldb_library(lldbUtility
|
||||||
RegularExpression.cpp
|
RegularExpression.cpp
|
||||||
Reproducer.cpp
|
Reproducer.cpp
|
||||||
ReproducerInstrumentation.cpp
|
ReproducerInstrumentation.cpp
|
||||||
|
ReproducerProvider.cpp
|
||||||
Scalar.cpp
|
Scalar.cpp
|
||||||
SelectHelper.cpp
|
SelectHelper.cpp
|
||||||
State.cpp
|
State.cpp
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "lldb/Utility/ProcessInfo.h"
|
#include "lldb/Utility/ProcessInfo.h"
|
||||||
|
|
||||||
#include "lldb/Utility/ArchSpec.h"
|
#include "lldb/Utility/ArchSpec.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "lldb/Utility/Stream.h"
|
#include "lldb/Utility/Stream.h"
|
||||||
#include "lldb/Utility/StreamString.h"
|
#include "lldb/Utility/StreamString.h"
|
||||||
#include "lldb/Utility/UserIDResolver.h"
|
#include "lldb/Utility/UserIDResolver.h"
|
||||||
|
@ -347,57 +348,6 @@ void llvm::yaml::MappingTraits<ProcessInstanceInfo>::mapping(
|
||||||
io.mapRequired("parent-pid", Info.m_parent_pid);
|
io.mapRequired("parent-pid", Info.m_parent_pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
|
|
||||||
ProcessInfoRecorder::Create(const FileSpec &filename) {
|
|
||||||
std::error_code ec;
|
|
||||||
auto recorder =
|
|
||||||
std::make_unique<ProcessInfoRecorder>(std::move(filename), ec);
|
|
||||||
if (ec)
|
|
||||||
return llvm::errorCodeToError(ec);
|
|
||||||
return std::move(recorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessInfoProvider::Keep() {
|
|
||||||
std::vector<std::string> files;
|
|
||||||
for (auto &recorder : m_process_info_recorders) {
|
|
||||||
recorder->Stop();
|
|
||||||
files.push_back(recorder->GetFilename().GetPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
|
|
||||||
std::error_code ec;
|
|
||||||
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
|
||||||
if (ec)
|
|
||||||
return;
|
|
||||||
llvm::yaml::Output yout(os);
|
|
||||||
yout << files;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessInfoProvider::Discard() { m_process_info_recorders.clear(); }
|
|
||||||
|
|
||||||
ProcessInfoRecorder *ProcessInfoProvider::GetNewProcessInfoRecorder() {
|
|
||||||
std::size_t i = m_process_info_recorders.size() + 1;
|
|
||||||
std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
|
|
||||||
llvm::Twine(i) + llvm::Twine(".yaml"))
|
|
||||||
.str();
|
|
||||||
auto recorder_or_error = ProcessInfoRecorder::Create(
|
|
||||||
GetRoot().CopyByAppendingPathComponent(filename));
|
|
||||||
if (!recorder_or_error) {
|
|
||||||
llvm::consumeError(recorder_or_error.takeError());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_process_info_recorders.push_back(std::move(*recorder_or_error));
|
|
||||||
return m_process_info_recorders.back().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessInfoRecorder::Record(const ProcessInstanceInfoList &process_infos) {
|
|
||||||
if (!m_record)
|
|
||||||
return;
|
|
||||||
llvm::yaml::Output yout(m_os);
|
|
||||||
yout << const_cast<ProcessInstanceInfoList &>(process_infos);
|
|
||||||
m_os.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Optional<ProcessInstanceInfoList>
|
llvm::Optional<ProcessInstanceInfoList>
|
||||||
repro::GetReplayProcessInstanceInfoList() {
|
repro::GetReplayProcessInstanceInfoList() {
|
||||||
|
@ -425,7 +375,3 @@ repro::GetReplayProcessInstanceInfoList() {
|
||||||
|
|
||||||
return infos;
|
return infos;
|
||||||
}
|
}
|
||||||
|
|
||||||
char ProcessInfoProvider::ID = 0;
|
|
||||||
const char *ProcessInfoProvider::Info::file = "process-info.yaml";
|
|
||||||
const char *ProcessInfoProvider::Info::name = "process-info";
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "lldb/Utility/Reproducer.h"
|
#include "lldb/Utility/Reproducer.h"
|
||||||
#include "lldb/Utility/LLDBAssert.h"
|
#include "lldb/Utility/LLDBAssert.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
|
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Threading.h"
|
#include "llvm/Support/Threading.h"
|
||||||
|
@ -263,58 +264,3 @@ bool Loader::HasFile(StringRef file) {
|
||||||
auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
|
auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
|
||||||
return (it != m_files.end()) && (*it == file);
|
return (it != m_files.end()) && (*it == file);
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Expected<std::unique_ptr<DataRecorder>>
|
|
||||||
DataRecorder::Create(const FileSpec &filename) {
|
|
||||||
std::error_code ec;
|
|
||||||
auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec);
|
|
||||||
if (ec)
|
|
||||||
return llvm::errorCodeToError(ec);
|
|
||||||
return std::move(recorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Expected<std::unique_ptr<YamlRecorder>>
|
|
||||||
YamlRecorder::Create(const FileSpec &filename) {
|
|
||||||
std::error_code ec;
|
|
||||||
auto recorder = std::make_unique<YamlRecorder>(std::move(filename), ec);
|
|
||||||
if (ec)
|
|
||||||
return llvm::errorCodeToError(ec);
|
|
||||||
return std::move(recorder);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VersionProvider::Keep() {
|
|
||||||
FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
|
|
||||||
std::error_code ec;
|
|
||||||
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
|
||||||
if (ec)
|
|
||||||
return;
|
|
||||||
os << m_version << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileProvider::RecordInterestingDirectory(const llvm::Twine &dir) {
|
|
||||||
if (m_collector)
|
|
||||||
m_collector->addFile(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileProvider::RecordInterestingDirectoryRecursive(const llvm::Twine &dir) {
|
|
||||||
if (m_collector)
|
|
||||||
m_collector->addDirectory(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProviderBase::anchor() {}
|
|
||||||
char CommandProvider::ID = 0;
|
|
||||||
char FileProvider::ID = 0;
|
|
||||||
char ProviderBase::ID = 0;
|
|
||||||
char VersionProvider::ID = 0;
|
|
||||||
char WorkingDirectoryProvider::ID = 0;
|
|
||||||
char HomeDirectoryProvider::ID = 0;
|
|
||||||
const char *CommandProvider::Info::file = "command-interpreter.yaml";
|
|
||||||
const char *CommandProvider::Info::name = "command-interpreter";
|
|
||||||
const char *FileProvider::Info::file = "files.yaml";
|
|
||||||
const char *FileProvider::Info::name = "files";
|
|
||||||
const char *VersionProvider::Info::file = "version.txt";
|
|
||||||
const char *VersionProvider::Info::name = "version";
|
|
||||||
const char *WorkingDirectoryProvider::Info::file = "cwd.txt";
|
|
||||||
const char *WorkingDirectoryProvider::Info::name = "cwd";
|
|
||||||
const char *HomeDirectoryProvider::Info::file = "home.txt";
|
|
||||||
const char *HomeDirectoryProvider::Info::name = "home";
|
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
//===-- Reproducer.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/ReproducerProvider.h"
|
||||||
|
#include "lldb/Utility/ProcessInfo.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
|
using namespace lldb_private;
|
||||||
|
using namespace lldb_private::repro;
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::yaml;
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<DataRecorder>>
|
||||||
|
DataRecorder::Create(const FileSpec &filename) {
|
||||||
|
std::error_code ec;
|
||||||
|
auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec);
|
||||||
|
if (ec)
|
||||||
|
return llvm::errorCodeToError(ec);
|
||||||
|
return std::move(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<YamlRecorder>>
|
||||||
|
YamlRecorder::Create(const FileSpec &filename) {
|
||||||
|
std::error_code ec;
|
||||||
|
auto recorder = std::make_unique<YamlRecorder>(std::move(filename), ec);
|
||||||
|
if (ec)
|
||||||
|
return llvm::errorCodeToError(ec);
|
||||||
|
return std::move(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VersionProvider::Keep() {
|
||||||
|
FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
|
||||||
|
std::error_code ec;
|
||||||
|
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
||||||
|
if (ec)
|
||||||
|
return;
|
||||||
|
os << m_version << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileProvider::RecordInterestingDirectory(const llvm::Twine &dir) {
|
||||||
|
if (m_collector)
|
||||||
|
m_collector->addFile(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileProvider::RecordInterestingDirectoryRecursive(const llvm::Twine &dir) {
|
||||||
|
if (m_collector)
|
||||||
|
m_collector->addDirectory(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
|
||||||
|
ProcessInfoRecorder::Create(const FileSpec &filename) {
|
||||||
|
std::error_code ec;
|
||||||
|
auto recorder =
|
||||||
|
std::make_unique<ProcessInfoRecorder>(std::move(filename), ec);
|
||||||
|
if (ec)
|
||||||
|
return llvm::errorCodeToError(ec);
|
||||||
|
return std::move(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessInfoProvider::Keep() {
|
||||||
|
std::vector<std::string> files;
|
||||||
|
for (auto &recorder : m_process_info_recorders) {
|
||||||
|
recorder->Stop();
|
||||||
|
files.push_back(recorder->GetFilename().GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
|
||||||
|
std::error_code ec;
|
||||||
|
llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
|
||||||
|
if (ec)
|
||||||
|
return;
|
||||||
|
llvm::yaml::Output yout(os);
|
||||||
|
yout << files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessInfoProvider::Discard() { m_process_info_recorders.clear(); }
|
||||||
|
|
||||||
|
ProcessInfoRecorder *ProcessInfoProvider::GetNewProcessInfoRecorder() {
|
||||||
|
std::size_t i = m_process_info_recorders.size() + 1;
|
||||||
|
std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
|
||||||
|
llvm::Twine(i) + llvm::Twine(".yaml"))
|
||||||
|
.str();
|
||||||
|
auto recorder_or_error = ProcessInfoRecorder::Create(
|
||||||
|
GetRoot().CopyByAppendingPathComponent(filename));
|
||||||
|
if (!recorder_or_error) {
|
||||||
|
llvm::consumeError(recorder_or_error.takeError());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_process_info_recorders.push_back(std::move(*recorder_or_error));
|
||||||
|
return m_process_info_recorders.back().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProcessInfoRecorder::Record(const ProcessInstanceInfoList &process_infos) {
|
||||||
|
if (!m_record)
|
||||||
|
return;
|
||||||
|
llvm::yaml::Output yout(m_os);
|
||||||
|
yout << const_cast<ProcessInstanceInfoList &>(process_infos);
|
||||||
|
m_os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProviderBase::anchor() {}
|
||||||
|
char CommandProvider::ID = 0;
|
||||||
|
char FileProvider::ID = 0;
|
||||||
|
char ProviderBase::ID = 0;
|
||||||
|
char VersionProvider::ID = 0;
|
||||||
|
char WorkingDirectoryProvider::ID = 0;
|
||||||
|
char HomeDirectoryProvider::ID = 0;
|
||||||
|
char ProcessInfoProvider::ID = 0;
|
||||||
|
const char *CommandProvider::Info::file = "command-interpreter.yaml";
|
||||||
|
const char *CommandProvider::Info::name = "command-interpreter";
|
||||||
|
const char *FileProvider::Info::file = "files.yaml";
|
||||||
|
const char *FileProvider::Info::name = "files";
|
||||||
|
const char *VersionProvider::Info::file = "version.txt";
|
||||||
|
const char *VersionProvider::Info::name = "version";
|
||||||
|
const char *WorkingDirectoryProvider::Info::file = "cwd.txt";
|
||||||
|
const char *WorkingDirectoryProvider::Info::name = "cwd";
|
||||||
|
const char *HomeDirectoryProvider::Info::file = "home.txt";
|
||||||
|
const char *HomeDirectoryProvider::Info::name = "home";
|
||||||
|
const char *ProcessInfoProvider::Info::file = "process-info.yaml";
|
||||||
|
const char *ProcessInfoProvider::Info::name = "process-info";
|
|
@ -9,13 +9,13 @@
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "lldb/Utility/FileSpec.h"
|
||||||
|
#include "lldb/Utility/Reproducer.h"
|
||||||
|
#include "lldb/Utility/ReproducerProvider.h"
|
||||||
#include "llvm/ADT/ScopeExit.h"
|
#include "llvm/ADT/ScopeExit.h"
|
||||||
#include "llvm/Support/Error.h"
|
#include "llvm/Support/Error.h"
|
||||||
#include "llvm/Testing/Support/Error.h"
|
#include "llvm/Testing/Support/Error.h"
|
||||||
|
|
||||||
#include "lldb/Utility/FileSpec.h"
|
|
||||||
#include "lldb/Utility/Reproducer.h"
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace lldb_private;
|
using namespace lldb_private;
|
||||||
using namespace lldb_private::repro;
|
using namespace lldb_private::repro;
|
||||||
|
|
Loading…
Reference in New Issue