1343181Sdim//===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
2343181Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343181Sdim//
7343181Sdim//===----------------------------------------------------------------------===//
8343181Sdim
9343181Sdim#include "lldb/Utility/Reproducer.h"
10343181Sdim#include "lldb/Utility/LLDBAssert.h"
11343181Sdim
12343181Sdim#include "llvm/Support/FileSystem.h"
13343181Sdim#include "llvm/Support/Threading.h"
14343181Sdim#include "llvm/Support/raw_ostream.h"
15343181Sdim
16343181Sdimusing namespace lldb_private;
17343181Sdimusing namespace lldb_private::repro;
18343181Sdimusing namespace llvm;
19343181Sdimusing namespace llvm::yaml;
20343181Sdim
21343181SdimReproducer &Reproducer::Instance() { return *InstanceImpl(); }
22343181Sdim
23343181Sdimllvm::Error Reproducer::Initialize(ReproducerMode mode,
24343181Sdim                                   llvm::Optional<FileSpec> root) {
25343181Sdim  lldbassert(!InstanceImpl() && "Already initialized.");
26343181Sdim  InstanceImpl().emplace();
27343181Sdim
28360784Sdim  // The environment can override the capture mode.
29360784Sdim  if (mode != ReproducerMode::Replay) {
30360784Sdim    std::string env =
31360784Sdim        llvm::StringRef(getenv("LLDB_CAPTURE_REPRODUCER")).lower();
32360784Sdim    if (env == "0" || env == "off")
33360784Sdim      mode = ReproducerMode::Off;
34360784Sdim    else if (env == "1" || env == "on")
35360784Sdim      mode = ReproducerMode::Capture;
36360784Sdim  }
37360784Sdim
38343181Sdim  switch (mode) {
39343181Sdim  case ReproducerMode::Capture: {
40343181Sdim    if (!root) {
41343181Sdim      SmallString<128> repro_dir;
42343181Sdim      auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
43343181Sdim      if (ec)
44343181Sdim        return make_error<StringError>(
45343181Sdim            "unable to create unique reproducer directory", ec);
46343181Sdim      root.emplace(repro_dir);
47343181Sdim    } else {
48343181Sdim      auto ec = sys::fs::create_directory(root->GetPath());
49343181Sdim      if (ec)
50343181Sdim        return make_error<StringError>("unable to create reproducer directory",
51343181Sdim                                       ec);
52343181Sdim    }
53343181Sdim    return Instance().SetCapture(root);
54343181Sdim  } break;
55343181Sdim  case ReproducerMode::Replay:
56343181Sdim    return Instance().SetReplay(root);
57343181Sdim  case ReproducerMode::Off:
58343181Sdim    break;
59343181Sdim  };
60343181Sdim
61343181Sdim  return Error::success();
62343181Sdim}
63343181Sdim
64353358Sdimbool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
65353358Sdim
66343181Sdimvoid Reproducer::Terminate() {
67343181Sdim  lldbassert(InstanceImpl() && "Already terminated.");
68343181Sdim  InstanceImpl().reset();
69343181Sdim}
70343181Sdim
71343181SdimOptional<Reproducer> &Reproducer::InstanceImpl() {
72343181Sdim  static Optional<Reproducer> g_reproducer;
73343181Sdim  return g_reproducer;
74343181Sdim}
75343181Sdim
76343181Sdimconst Generator *Reproducer::GetGenerator() const {
77343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
78343181Sdim  if (m_generator)
79343181Sdim    return &(*m_generator);
80343181Sdim  return nullptr;
81343181Sdim}
82343181Sdim
83343181Sdimconst Loader *Reproducer::GetLoader() const {
84343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
85343181Sdim  if (m_loader)
86343181Sdim    return &(*m_loader);
87343181Sdim  return nullptr;
88343181Sdim}
89343181Sdim
90343181SdimGenerator *Reproducer::GetGenerator() {
91343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
92343181Sdim  if (m_generator)
93343181Sdim    return &(*m_generator);
94343181Sdim  return nullptr;
95343181Sdim}
96343181Sdim
97343181SdimLoader *Reproducer::GetLoader() {
98343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
99343181Sdim  if (m_loader)
100343181Sdim    return &(*m_loader);
101343181Sdim  return nullptr;
102343181Sdim}
103343181Sdim
104343181Sdimllvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
105343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
106343181Sdim
107343181Sdim  if (root && m_loader)
108343181Sdim    return make_error<StringError>(
109343181Sdim        "cannot generate a reproducer when replay one",
110343181Sdim        inconvertibleErrorCode());
111343181Sdim
112343181Sdim  if (!root) {
113343181Sdim    m_generator.reset();
114343181Sdim    return Error::success();
115343181Sdim  }
116343181Sdim
117343181Sdim  m_generator.emplace(*root);
118343181Sdim  return Error::success();
119343181Sdim}
120343181Sdim
121343181Sdimllvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
122343181Sdim  std::lock_guard<std::mutex> guard(m_mutex);
123343181Sdim
124343181Sdim  if (root && m_generator)
125343181Sdim    return make_error<StringError>(
126343181Sdim        "cannot replay a reproducer when generating one",
127343181Sdim        inconvertibleErrorCode());
128343181Sdim
129343181Sdim  if (!root) {
130343181Sdim    m_loader.reset();
131343181Sdim    return Error::success();
132343181Sdim  }
133343181Sdim
134343181Sdim  m_loader.emplace(*root);
135343181Sdim  if (auto e = m_loader->LoadIndex())
136343181Sdim    return e;
137343181Sdim
138343181Sdim  return Error::success();
139343181Sdim}
140343181Sdim
141343181SdimFileSpec Reproducer::GetReproducerPath() const {
142343181Sdim  if (auto g = GetGenerator())
143343181Sdim    return g->GetRoot();
144343181Sdim  if (auto l = GetLoader())
145343181Sdim    return l->GetRoot();
146343181Sdim  return {};
147343181Sdim}
148343181Sdim
149360784Sdimstatic FileSpec MakeAbsolute(FileSpec file_spec) {
150360784Sdim  SmallString<128> path;
151360784Sdim  file_spec.GetPath(path, false);
152360784Sdim  llvm::sys::fs::make_absolute(path);
153360784Sdim  return FileSpec(path, file_spec.GetPathStyle());
154360784Sdim}
155343181Sdim
156360784SdimGenerator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) {
157360784Sdim  GetOrCreate<repro::WorkingDirectoryProvider>();
158360784Sdim}
159343181Sdim
160360784SdimGenerator::~Generator() {
161360784Sdim  if (!m_done)
162360784Sdim    Discard();
163360784Sdim}
164360784Sdim
165343181SdimProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
166343181Sdim  std::lock_guard<std::mutex> lock(m_providers_mutex);
167343181Sdim  std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
168343181Sdim      provider->DynamicClassID(), std::move(provider));
169343181Sdim  auto e = m_providers.insert(std::move(key_value));
170343181Sdim  return e.first->getSecond().get();
171343181Sdim}
172343181Sdim
173343181Sdimvoid Generator::Keep() {
174343181Sdim  assert(!m_done);
175343181Sdim  m_done = true;
176343181Sdim
177343181Sdim  for (auto &provider : m_providers)
178343181Sdim    provider.second->Keep();
179343181Sdim
180343181Sdim  AddProvidersToIndex();
181343181Sdim}
182343181Sdim
183343181Sdimvoid Generator::Discard() {
184343181Sdim  assert(!m_done);
185343181Sdim  m_done = true;
186343181Sdim
187343181Sdim  for (auto &provider : m_providers)
188343181Sdim    provider.second->Discard();
189343181Sdim
190343181Sdim  llvm::sys::fs::remove_directories(m_root.GetPath());
191343181Sdim}
192343181Sdim
193343181Sdimconst FileSpec &Generator::GetRoot() const { return m_root; }
194343181Sdim
195343181Sdimvoid Generator::AddProvidersToIndex() {
196343181Sdim  FileSpec index = m_root;
197343181Sdim  index.AppendPathComponent("index.yaml");
198343181Sdim
199343181Sdim  std::error_code EC;
200360784Sdim  auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC,
201360784Sdim                                               sys::fs::OpenFlags::OF_None);
202343181Sdim  yaml::Output yout(*strm);
203343181Sdim
204353358Sdim  std::vector<std::string> files;
205353358Sdim  files.reserve(m_providers.size());
206343181Sdim  for (auto &provider : m_providers) {
207353358Sdim    files.emplace_back(provider.second->GetFile());
208343181Sdim  }
209353358Sdim
210353358Sdim  yout << files;
211343181Sdim}
212343181Sdim
213360784SdimLoader::Loader(FileSpec root)
214360784Sdim    : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {}
215343181Sdim
216343181Sdimllvm::Error Loader::LoadIndex() {
217343181Sdim  if (m_loaded)
218343181Sdim    return llvm::Error::success();
219343181Sdim
220343181Sdim  FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
221343181Sdim
222343181Sdim  auto error_or_file = MemoryBuffer::getFile(index.GetPath());
223343181Sdim  if (auto err = error_or_file.getError())
224343181Sdim    return make_error<StringError>("unable to load reproducer index", err);
225343181Sdim
226343181Sdim  yaml::Input yin((*error_or_file)->getBuffer());
227353358Sdim  yin >> m_files;
228343181Sdim  if (auto err = yin.error())
229343181Sdim    return make_error<StringError>("unable to read reproducer index", err);
230343181Sdim
231353358Sdim  // Sort files to speed up search.
232353358Sdim  llvm::sort(m_files);
233343181Sdim
234353358Sdim  // Remember that we've loaded the index.
235343181Sdim  m_loaded = true;
236343181Sdim
237343181Sdim  return llvm::Error::success();
238343181Sdim}
239343181Sdim
240353358Sdimbool Loader::HasFile(StringRef file) {
241343181Sdim  assert(m_loaded);
242353358Sdim  auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
243353358Sdim  return (it != m_files.end()) && (*it == file);
244353358Sdim}
245343181Sdim
246353358Sdimllvm::Expected<std::unique_ptr<DataRecorder>>
247353358SdimDataRecorder::Create(const FileSpec &filename) {
248353358Sdim  std::error_code ec;
249360784Sdim  auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec);
250353358Sdim  if (ec)
251353358Sdim    return llvm::errorCodeToError(ec);
252353358Sdim  return std::move(recorder);
253353358Sdim}
254343181Sdim
255353358SdimDataRecorder *CommandProvider::GetNewDataRecorder() {
256353358Sdim  std::size_t i = m_data_recorders.size() + 1;
257353358Sdim  std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
258360784Sdim                          llvm::Twine(i) + llvm::Twine(".yaml"))
259353358Sdim                             .str();
260353358Sdim  auto recorder_or_error =
261353358Sdim      DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));
262353358Sdim  if (!recorder_or_error) {
263353358Sdim    llvm::consumeError(recorder_or_error.takeError());
264353358Sdim    return nullptr;
265353358Sdim  }
266353358Sdim
267353358Sdim  m_data_recorders.push_back(std::move(*recorder_or_error));
268353358Sdim  return m_data_recorders.back().get();
269343181Sdim}
270343181Sdim
271353358Sdimvoid CommandProvider::Keep() {
272353358Sdim  std::vector<std::string> files;
273353358Sdim  for (auto &recorder : m_data_recorders) {
274353358Sdim    recorder->Stop();
275353358Sdim    files.push_back(recorder->GetFilename().GetPath());
276353358Sdim  }
277353358Sdim
278353358Sdim  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
279353358Sdim  std::error_code ec;
280360784Sdim  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
281353358Sdim  if (ec)
282353358Sdim    return;
283353358Sdim  yaml::Output yout(os);
284353358Sdim  yout << files;
285353358Sdim}
286353358Sdim
287353358Sdimvoid CommandProvider::Discard() { m_data_recorders.clear(); }
288353358Sdim
289353358Sdimvoid VersionProvider::Keep() {
290353358Sdim  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
291353358Sdim  std::error_code ec;
292360784Sdim  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
293353358Sdim  if (ec)
294353358Sdim    return;
295353358Sdim  os << m_version << "\n";
296353358Sdim}
297353358Sdim
298360784Sdimvoid WorkingDirectoryProvider::Keep() {
299360784Sdim  FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
300360784Sdim  std::error_code ec;
301360784Sdim  llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
302360784Sdim  if (ec)
303360784Sdim    return;
304360784Sdim  os << m_cwd << "\n";
305360784Sdim}
306360784Sdim
307343181Sdimvoid ProviderBase::anchor() {}
308353358Sdimchar CommandProvider::ID = 0;
309353358Sdimchar FileProvider::ID = 0;
310360784Sdimchar ProviderBase::ID = 0;
311353358Sdimchar VersionProvider::ID = 0;
312360784Sdimchar WorkingDirectoryProvider::ID = 0;
313353358Sdimconst char *CommandProvider::Info::file = "command-interpreter.yaml";
314353358Sdimconst char *CommandProvider::Info::name = "command-interpreter";
315353358Sdimconst char *FileProvider::Info::file = "files.yaml";
316353358Sdimconst char *FileProvider::Info::name = "files";
317353358Sdimconst char *VersionProvider::Info::file = "version.txt";
318353358Sdimconst char *VersionProvider::Info::name = "version";
319360784Sdimconst char *WorkingDirectoryProvider::Info::file = "cwd.txt";
320360784Sdimconst char *WorkingDirectoryProvider::Info::name = "cwd";
321