]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/lldb/source/Utility/Reproducer.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / lldb / source / Utility / Reproducer.cpp
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 llvm::Expected<std::unique_ptr<YamlRecorder>>
276 YamlRecorder::Create(const FileSpec &filename) {
277   std::error_code ec;
278   auto recorder = std::make_unique<YamlRecorder>(std::move(filename), ec);
279   if (ec)
280     return llvm::errorCodeToError(ec);
281   return std::move(recorder);
282 }
283
284 void VersionProvider::Keep() {
285   FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
286   std::error_code ec;
287   llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
288   if (ec)
289     return;
290   os << m_version << "\n";
291 }
292
293 void WorkingDirectoryProvider::Keep() {
294   FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file);
295   std::error_code ec;
296   llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text);
297   if (ec)
298     return;
299   os << m_cwd << "\n";
300 }
301
302 void FileProvider::recordInterestingDirectory(const llvm::Twine &dir) {
303   if (m_collector)
304     m_collector->addDirectory(dir);
305 }
306
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";