1 //===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
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 Reproducer &Reproducer::Instance() { return *InstanceImpl(); }
22 
23 llvm::Error Reproducer::Initialize(ReproducerMode mode,
24                                    llvm::Optional<FileSpec> root) {
25   lldbassert(!InstanceImpl() && "Already initialized.");
26   InstanceImpl().emplace();
27 
28   switch (mode) {
29   case ReproducerMode::Capture: {
30     if (!root) {
31       SmallString<128> repro_dir;
32       auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir);
33       if (ec)
34         return make_error<StringError>(
35             "unable to create unique reproducer directory", ec);
36       root.emplace(repro_dir);
37     } else {
38       auto ec = sys::fs::create_directory(root->GetPath());
39       if (ec)
40         return make_error<StringError>("unable to create reproducer directory",
41                                        ec);
42     }
43     return Instance().SetCapture(root);
44   } break;
45   case ReproducerMode::Replay:
46     return Instance().SetReplay(root);
47   case ReproducerMode::Off:
48     break;
49   };
50 
51   return Error::success();
52 }
53 
54 void Reproducer::Terminate() {
55   lldbassert(InstanceImpl() && "Already terminated.");
56   InstanceImpl().reset();
57 }
58 
59 Optional<Reproducer> &Reproducer::InstanceImpl() {
60   static Optional<Reproducer> g_reproducer;
61   return g_reproducer;
62 }
63 
64 const Generator *Reproducer::GetGenerator() const {
65   std::lock_guard<std::mutex> guard(m_mutex);
66   if (m_generator)
67     return &(*m_generator);
68   return nullptr;
69 }
70 
71 const Loader *Reproducer::GetLoader() const {
72   std::lock_guard<std::mutex> guard(m_mutex);
73   if (m_loader)
74     return &(*m_loader);
75   return nullptr;
76 }
77 
78 Generator *Reproducer::GetGenerator() {
79   std::lock_guard<std::mutex> guard(m_mutex);
80   if (m_generator)
81     return &(*m_generator);
82   return nullptr;
83 }
84 
85 Loader *Reproducer::GetLoader() {
86   std::lock_guard<std::mutex> guard(m_mutex);
87   if (m_loader)
88     return &(*m_loader);
89   return nullptr;
90 }
91 
92 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
93   std::lock_guard<std::mutex> guard(m_mutex);
94 
95   if (root && m_loader)
96     return make_error<StringError>(
97         "cannot generate a reproducer when replay one",
98         inconvertibleErrorCode());
99 
100   if (!root) {
101     m_generator.reset();
102     return Error::success();
103   }
104 
105   m_generator.emplace(*root);
106   return Error::success();
107 }
108 
109 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
110   std::lock_guard<std::mutex> guard(m_mutex);
111 
112   if (root && m_generator)
113     return make_error<StringError>(
114         "cannot replay a reproducer when generating one",
115         inconvertibleErrorCode());
116 
117   if (!root) {
118     m_loader.reset();
119     return Error::success();
120   }
121 
122   m_loader.emplace(*root);
123   if (auto e = m_loader->LoadIndex())
124     return e;
125 
126   return Error::success();
127 }
128 
129 FileSpec Reproducer::GetReproducerPath() const {
130   if (auto g = GetGenerator())
131     return g->GetRoot();
132   if (auto l = GetLoader())
133     return l->GetRoot();
134   return {};
135 }
136 
137 Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {}
138 
139 Generator::~Generator() {}
140 
141 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
142   std::lock_guard<std::mutex> lock(m_providers_mutex);
143   std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
144       provider->DynamicClassID(), std::move(provider));
145   auto e = m_providers.insert(std::move(key_value));
146   return e.first->getSecond().get();
147 }
148 
149 void Generator::Keep() {
150   assert(!m_done);
151   m_done = true;
152 
153   for (auto &provider : m_providers)
154     provider.second->Keep();
155 
156   AddProvidersToIndex();
157 }
158 
159 void Generator::Discard() {
160   assert(!m_done);
161   m_done = true;
162 
163   for (auto &provider : m_providers)
164     provider.second->Discard();
165 
166   llvm::sys::fs::remove_directories(m_root.GetPath());
167 }
168 
169 const FileSpec &Generator::GetRoot() const { return m_root; }
170 
171 void Generator::AddProvidersToIndex() {
172   FileSpec index = m_root;
173   index.AppendPathComponent("index.yaml");
174 
175   std::error_code EC;
176   auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC,
177                                                 sys::fs::OpenFlags::F_None);
178   yaml::Output yout(*strm);
179 
180   std::vector<std::string> files;
181   files.reserve(m_providers.size());
182   for (auto &provider : m_providers) {
183     files.emplace_back(provider.second->GetFile());
184   }
185 
186   yout << files;
187 }
188 
189 Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {}
190 
191 llvm::Error Loader::LoadIndex() {
192   if (m_loaded)
193     return llvm::Error::success();
194 
195   FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
196 
197   auto error_or_file = MemoryBuffer::getFile(index.GetPath());
198   if (auto err = error_or_file.getError())
199     return make_error<StringError>("unable to load reproducer index", err);
200 
201   yaml::Input yin((*error_or_file)->getBuffer());
202   yin >> m_files;
203   if (auto err = yin.error())
204     return make_error<StringError>("unable to read reproducer index", err);
205 
206   // Sort files to speed up search.
207   llvm::sort(m_files);
208 
209   // Remember that we've loaded the index.
210   m_loaded = true;
211 
212   return llvm::Error::success();
213 }
214 
215 bool Loader::HasFile(StringRef file) {
216   assert(m_loaded);
217   auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str());
218   return (it != m_files.end()) && (*it == file);
219 }
220 
221 void ProviderBase::anchor() {}
222 char ProviderBase::ID = 0;
223