1 //===-- Reproducer.cpp ----------------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Utility/Reproducer.h"
10 #include "lldb/Utility/LLDBAssert.h"
11 
12 #include "llvm/Support/FileSystem.h"
13 #include "llvm/Support/Threading.h"
14 #include "llvm/Support/raw_ostream.h"
15 
16 using namespace lldb_private;
17 using namespace lldb_private::repro;
18 using namespace llvm;
19 using namespace llvm::yaml;
20 
21 static llvm::Optional<bool> GetEnv(const char *var) {
22   std::string val = llvm::StringRef(getenv(var)).lower();
23   if (val == "0" || val == "off")
24     return false;
25   if (val == "1" || val == "on")
26     return true;
27   return {};
28 }
29 
30 Reproducer &Reproducer::Instance() { return *InstanceImpl(); }
31 
32 llvm::Error Reproducer::Initialize(ReproducerMode mode,
33                                    llvm::Optional<FileSpec> root) {
34   lldbassert(!InstanceImpl() && "Already initialized.");
35   InstanceImpl().emplace();
36 
37   // The environment can override the capture mode.
38   if (mode != ReproducerMode::Replay) {
39     if (llvm::Optional<bool> override = GetEnv("LLDB_CAPTURE_REPRODUCER")) {
40       if (*override)
41         mode = ReproducerMode::Capture;
42       else
43         mode = ReproducerMode::Off;
44     }
45   }
46 
47   switch (mode) {
48   case ReproducerMode::Capture: {
49     if (!root) {
50       SmallString<128> repro_dir;
51       auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
52       if (ec)
53         return make_error<StringError>(
54             "unable to create unique reproducer directory", ec);
55       root.emplace(repro_dir);
56     } else {
57       auto ec = sys::fs::create_directory(root->GetPath());
58       if (ec)
59         return make_error<StringError>("unable to create reproducer directory",
60                                        ec);
61     }
62     return Instance().SetCapture(root);
63   } break;
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:
69     break;
70   };
71 
72   return Error::success();
73 }
74 
75 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
76 
77 void Reproducer::Terminate() {
78   lldbassert(InstanceImpl() && "Already terminated.");
79   InstanceImpl().reset();
80 }
81 
82 Optional<Reproducer> &Reproducer::InstanceImpl() {
83   static Optional<Reproducer> g_reproducer;
84   return g_reproducer;
85 }
86 
87 const Generator *Reproducer::GetGenerator() const {
88   std::lock_guard<std::mutex> guard(m_mutex);
89   if (m_generator)
90     return &(*m_generator);
91   return nullptr;
92 }
93 
94 const Loader *Reproducer::GetLoader() const {
95   std::lock_guard<std::mutex> guard(m_mutex);
96   if (m_loader)
97     return &(*m_loader);
98   return nullptr;
99 }
100 
101 Generator *Reproducer::GetGenerator() {
102   std::lock_guard<std::mutex> guard(m_mutex);
103   if (m_generator)
104     return &(*m_generator);
105   return nullptr;
106 }
107 
108 Loader *Reproducer::GetLoader() {
109   std::lock_guard<std::mutex> guard(m_mutex);
110   if (m_loader)
111     return &(*m_loader);
112   return nullptr;
113 }
114 
115 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
116   std::lock_guard<std::mutex> guard(m_mutex);
117 
118   if (root && m_loader)
119     return make_error<StringError>(
120         "cannot generate a reproducer when replay one",
121         inconvertibleErrorCode());
122 
123   if (!root) {
124     m_generator.reset();
125     return Error::success();
126   }
127 
128   m_generator.emplace(*root);
129   return Error::success();
130 }
131 
132 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root, bool passive) {
133   std::lock_guard<std::mutex> guard(m_mutex);
134 
135   if (root && m_generator)
136     return make_error<StringError>(
137         "cannot replay a reproducer when generating one",
138         inconvertibleErrorCode());
139 
140   if (!root) {
141     m_loader.reset();
142     return Error::success();
143   }
144 
145   m_loader.emplace(*root, passive);
146   if (auto e = m_loader->LoadIndex())
147     return e;
148 
149   return Error::success();
150 }
151 
152 FileSpec Reproducer::GetReproducerPath() const {
153   if (auto g = GetGenerator())
154     return g->GetRoot();
155   if (auto l = GetLoader())
156     return l->GetRoot();
157   return {};
158 }
159 
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());
165 }
166 
167 Generator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) {
168   GetOrCreate<repro::WorkingDirectoryProvider>();
169 }
170 
171 Generator::~Generator() {
172   if (!m_done) {
173     if (m_auto_generate)
174       Keep();
175     else
176       Discard();
177   }
178 }
179 
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();
186 }
187 
188 void Generator::Keep() {
189   assert(!m_done);
190   m_done = true;
191 
192   for (auto &provider : m_providers)
193     provider.second->Keep();
194 
195   AddProvidersToIndex();
196 }
197 
198 void Generator::Discard() {
199   assert(!m_done);
200   m_done = true;
201 
202   for (auto &provider : m_providers)
203     provider.second->Discard();
204 
205   llvm::sys::fs::remove_directories(m_root.GetPath());
206 }
207 
208 void Generator::SetAutoGenerate(bool b) { m_auto_generate = b; }
209 
210 bool Generator::IsAutoGenerate() const { return m_auto_generate; }
211 
212 const FileSpec &Generator::GetRoot() const { return m_root; }
213 
214 void Generator::AddProvidersToIndex() {
215   FileSpec index = m_root;
216   index.AppendPathComponent("index.yaml");
217 
218   std::error_code EC;
219   auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC,
220                                                sys::fs::OpenFlags::OF_None);
221   yaml::Output yout(*strm);
222 
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());
227   }
228 
229   yout << files;
230 }
231 
232 Loader::Loader(FileSpec root, bool passive)
233     : m_root(MakeAbsolute(std::move(root))), m_loaded(false),
234       m_passive_replay(passive) {}
235 
236 llvm::Error Loader::LoadIndex() {
237   if (m_loaded)
238     return llvm::Error::success();
239 
240   FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
241 
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);
245 
246   yaml::Input yin((*error_or_file)->getBuffer());
247   yin >> m_files;
248   if (auto err = yin.error())
249     return make_error<StringError>("unable to read reproducer index", err);
250 
251   // Sort files to speed up search.
252   llvm::sort(m_files);
253 
254   // Remember that we've loaded the index.
255   m_loaded = true;
256 
257   return llvm::Error::success();
258 }
259 
260 bool Loader::HasFile(StringRef file) {
261   assert(m_loaded);
262   auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
263   return (it != m_files.end()) && (*it == file);
264 }
265 
266 llvm::Expected<std::unique_ptr<DataRecorder>>
267 DataRecorder::Create(const FileSpec &filename) {
268   std::error_code ec;
269   auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec);
270   if (ec)
271     return llvm::errorCodeToError(ec);
272   return std::move(recorder);
273 }
274 
275 DataRecorder *CommandProvider::GetNewDataRecorder() {
276   std::size_t i = m_data_recorders.size() + 1;
277   std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") +
278                           llvm::Twine(i) + llvm::Twine(".yaml"))
279                              .str();
280   auto recorder_or_error =
281       DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename));
282   if (!recorder_or_error) {
283     llvm::consumeError(recorder_or_error.takeError());
284     return nullptr;
285   }
286 
287   m_data_recorders.push_back(std::move(*recorder_or_error));
288   return m_data_recorders.back().get();
289 }
290 
291 void CommandProvider::Keep() {
292   std::vector<std::string> files;
293   for (auto &recorder : m_data_recorders) {
294     recorder->Stop();
295     files.push_back(recorder->GetFilename().GetPath());
296   }
297 
298   FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
299   std::error_code ec;
300   llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
301   if (ec)
302     return;
303   yaml::Output yout(os);
304   yout << files;
305 }
306 
307 void CommandProvider::Discard() { m_data_recorders.clear(); }
308 
309 void VersionProvider::Keep() {
310   FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
311   std::error_code ec;
312   llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
313   if (ec)
314     return;
315   os << m_version << "\n";
316 }
317 
318 void WorkingDirectoryProvider::Keep() {
319   FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
320   std::error_code ec;
321   llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
322   if (ec)
323     return;
324   os << m_cwd << "\n";
325 }
326 
327 void FileProvider::recordInterestingDirectory(const llvm::Twine &dir) {
328   if (m_collector)
329     m_collector->addDirectory(dir);
330 }
331 
332 void ProviderBase::anchor() {}
333 char CommandProvider::ID = 0;
334 char FileProvider::ID = 0;
335 char ProviderBase::ID = 0;
336 char VersionProvider::ID = 0;
337 char WorkingDirectoryProvider::ID = 0;
338 const char *CommandProvider::Info::file = "command-interpreter.yaml";
339 const char *CommandProvider::Info::name = "command-interpreter";
340 const char *FileProvider::Info::file = "files.yaml";
341 const char *FileProvider::Info::name = "files";
342 const char *VersionProvider::Info::file = "version.txt";
343 const char *VersionProvider::Info::name = "version";
344 const char *WorkingDirectoryProvider::Info::file = "cwd.txt";
345 const char *WorkingDirectoryProvider::Info::name = "cwd";
346