1 //===-- Reproducer.cpp ----------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Utility/Reproducer.h"
10 #include "lldb/Utility/LLDBAssert.h"
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Threading.h"
14 #include "llvm/Support/raw_ostream.h"
16 using namespace lldb_private;
17 using namespace lldb_private::repro;
19 using namespace llvm::yaml;
21 static llvm::Optional<bool> GetEnv(const char *var) {
22 std::string val = llvm::StringRef(getenv(var)).lower();
23 if (val == "0" || val == "off")
25 if (val == "1" || val == "on")
30 Reproducer &Reproducer::Instance() { return *InstanceImpl(); }
32 llvm::Error Reproducer::Initialize(ReproducerMode mode,
33 llvm::Optional<FileSpec> root) {
34 lldbassert(!InstanceImpl() && "Already initialized.");
35 InstanceImpl().emplace();
37 // The environment can override the capture mode.
38 if (mode != ReproducerMode::Replay) {
39 if (llvm::Optional<bool> override = GetEnv("LLDB_CAPTURE_REPRODUCER")) {
41 mode = ReproducerMode::Capture;
43 mode = ReproducerMode::Off;
48 case ReproducerMode::Capture: {
50 SmallString<128> repro_dir;
51 auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
53 return make_error<StringError>(
54 "unable to create unique reproducer directory", ec);
55 root.emplace(repro_dir);
57 auto ec = sys::fs::create_directory(root->GetPath());
59 return make_error<StringError>("unable to create reproducer directory",
62 return Instance().SetCapture(root);
64 case ReproducerMode::Replay:
65 return Instance().SetReplay(root, /*passive*/ false);
66 case ReproducerMode::PassiveReplay:
67 return Instance().SetReplay(root, /*passive*/ true);
68 case ReproducerMode::Off:
72 return Error::success();
75 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
77 void Reproducer::Terminate() {
78 lldbassert(InstanceImpl() && "Already terminated.");
79 InstanceImpl().reset();
82 Optional<Reproducer> &Reproducer::InstanceImpl() {
83 static Optional<Reproducer> g_reproducer;
87 const Generator *Reproducer::GetGenerator() const {
88 std::lock_guard<std::mutex> guard(m_mutex);
90 return &(*m_generator);
94 const Loader *Reproducer::GetLoader() const {
95 std::lock_guard<std::mutex> guard(m_mutex);
101 Generator *Reproducer::GetGenerator() {
102 std::lock_guard<std::mutex> guard(m_mutex);
104 return &(*m_generator);
108 Loader *Reproducer::GetLoader() {
109 std::lock_guard<std::mutex> guard(m_mutex);
115 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
116 std::lock_guard<std::mutex> guard(m_mutex);
118 if (root && m_loader)
119 return make_error<StringError>(
120 "cannot generate a reproducer when replay one",
121 inconvertibleErrorCode());
125 return Error::success();
128 m_generator.emplace(*root);
129 return Error::success();
132 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) {
133 std::lock_guard<std::mutex> guard(m_mutex);
135 if (root && m_generator)
136 return make_error<StringError>(
137 "cannot replay a reproducer when generating one",
138 inconvertibleErrorCode());
142 return Error::success();
145 m_loader.emplace(*root, passive);
146 if (auto e = m_loader->LoadIndex())
149 return Error::success();
152 FileSpec Reproducer::GetReproducerPath() const {
153 if (auto g = GetGenerator())
155 if (auto l = GetLoader())
160 static FileSpec MakeAbsolute(FileSpec file_spec) {
161 SmallString<128> path;
162 file_spec.GetPath(path, false);
163 llvm::sys::fs::make_absolute(path);
164 return FileSpec(path, file_spec.GetPathStyle());
167 Generator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) {
168 GetOrCreate<repro::WorkingDirectoryProvider>();
171 Generator::~Generator() {
180 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
181 std::lock_guard<std::mutex> lock(m_providers_mutex);
182 std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
183 provider->DynamicClassID(), std::move(provider));
184 auto e = m_providers.insert(std::move(key_value));
185 return e.first->getSecond().get();
188 void Generator::Keep() {
192 for (auto &provider : m_providers)
193 provider.second->Keep();
195 AddProvidersToIndex();
198 void Generator::Discard() {
202 for (auto &provider : m_providers)
203 provider.second->Discard();
205 llvm::sys::fs::remove_directories(m_root.GetPath());
208 void Generator::SetAutoGenerate(bool b) { m_auto_generate = b; }
210 bool Generator::IsAutoGenerate() const { return m_auto_generate; }
212 const FileSpec &Generator::GetRoot() const { return m_root; }
214 void Generator::AddProvidersToIndex() {
215 FileSpec index = m_root;
216 index.AppendPathComponent("index.yaml");
219 auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC,
220 sys::fs::OpenFlags::OF_None);
221 yaml::Output yout(*strm);
223 std::vector<std::string> files;
224 files.reserve(m_providers.size());
225 for (auto &provider : m_providers) {
226 files.emplace_back(provider.second->GetFile());
232 Loader::Loader(FileSpec root, bool passive)
233 : m_root(MakeAbsolute(std::move(root))), m_loaded(false),
234 m_passive_replay(passive) {}
236 llvm::Error Loader::LoadIndex() {
238 return llvm::Error::success();
240 FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
242 auto error_or_file = MemoryBuffer::getFile(index.GetPath());
243 if (auto err = error_or_file.getError())
244 return make_error<StringError>("unable to load reproducer index", err);
246 yaml::Input yin((*error_or_file)->getBuffer());
248 if (auto err = yin.error())
249 return make_error<StringError>("unable to read reproducer index", err);
251 // Sort files to speed up search.
254 // Remember that we've loaded the index.
257 return llvm::Error::success();
260 bool Loader::HasFile(StringRef file) {
262 auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
263 return (it != m_files.end()) && (*it == file);
266 llvm::Expected<std::unique_ptr<DataRecorder>>
267 DataRecorder::Create(const FileSpec &filename) {
269 auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec);
271 return llvm::errorCodeToError(ec);
272 return std::move(recorder);
275 llvm::Expected<std::unique_ptr<YamlRecorder>>
276 YamlRecorder::Create(const FileSpec &filename) {
278 auto recorder = std::make_unique<YamlRecorder>(std::move(filename), ec);
280 return llvm::errorCodeToError(ec);
281 return std::move(recorder);
284 void VersionProvider::Keep() {
285 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
287 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
290 os << m_version << "\n";
293 void WorkingDirectoryProvider::Keep() {
294 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
296 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
302 void FileProvider::recordInterestingDirectory(const llvm::Twine &dir) {
304 m_collector->addDirectory(dir);
307 void ProviderBase::anchor() {}
308 char CommandProvider::ID = 0;
309 char FileProvider::ID = 0;
310 char ProviderBase::ID = 0;
311 char VersionProvider::ID = 0;
312 char WorkingDirectoryProvider::ID = 0;
313 const char *CommandProvider::Info::file = "command-interpreter.yaml";
314 const char *CommandProvider::Info::name = "command-interpreter";
315 const char *FileProvider::Info::file = "files.yaml";
316 const char *FileProvider::Info::name = "files";
317 const char *VersionProvider::Info::file = "version.txt";
318 const char *VersionProvider::Info::name = "version";
319 const char *WorkingDirectoryProvider::Info::file = "cwd.txt";
320 const char *WorkingDirectoryProvider::Info::name = "cwd";