1 //===-- Reproducer.cpp ------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "lldb/Utility/Reproducer.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() {
22   static Reproducer g_reproducer;
23   return g_reproducer;
24 }
25 
26 const Generator *Reproducer::GetGenerator() const {
27   std::lock_guard<std::mutex> guard(m_mutex);
28   if (m_generator)
29     return &(*m_generator);
30   return nullptr;
31 }
32 
33 const Loader *Reproducer::GetLoader() const {
34   std::lock_guard<std::mutex> guard(m_mutex);
35   if (m_loader)
36     return &(*m_loader);
37   return nullptr;
38 }
39 
40 Generator *Reproducer::GetGenerator() {
41   std::lock_guard<std::mutex> guard(m_mutex);
42   if (m_generator)
43     return &(*m_generator);
44   return nullptr;
45 }
46 
47 Loader *Reproducer::GetLoader() {
48   std::lock_guard<std::mutex> guard(m_mutex);
49   if (m_loader)
50     return &(*m_loader);
51   return nullptr;
52 }
53 
54 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) {
55   std::lock_guard<std::mutex> guard(m_mutex);
56 
57   if (root && m_loader)
58     return make_error<StringError>(
59         "cannot generate a reproducer when replay one",
60         inconvertibleErrorCode());
61 
62   if (root)
63     m_generator.emplace(*root);
64   else
65     m_generator.reset();
66 
67   return Error::success();
68 }
69 
70 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) {
71   std::lock_guard<std::mutex> guard(m_mutex);
72 
73   if (root && m_generator)
74     return make_error<StringError>(
75         "cannot replay a reproducer when generating one",
76         inconvertibleErrorCode());
77 
78   if (root) {
79     m_loader.emplace(*root);
80     if (auto e = m_loader->LoadIndex())
81       return e;
82   } else {
83     m_loader.reset();
84   }
85 
86   return Error::success();
87 }
88 
89 FileSpec Reproducer::GetReproducerPath() const {
90   if (auto g = GetGenerator())
91     return g->GetRoot();
92   if (auto l = GetLoader())
93     return l->GetRoot();
94   return {};
95 }
96 
97 Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {}
98 
99 Generator::~Generator() {}
100 
101 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) {
102   std::lock_guard<std::mutex> lock(m_providers_mutex);
103   std::pair<const void *, std::unique_ptr<ProviderBase>> key_value(
104       provider->DynamicClassID(), std::move(provider));
105   auto e = m_providers.insert(std::move(key_value));
106   return e.first->getSecond().get();
107 }
108 
109 void Generator::Keep() {
110   assert(!m_done);
111   m_done = true;
112 
113   for (auto &provider : m_providers)
114     provider.second->Keep();
115 
116   AddProvidersToIndex();
117 }
118 
119 void Generator::Discard() {
120   assert(!m_done);
121   m_done = true;
122 
123   for (auto &provider : m_providers)
124     provider.second->Discard();
125 
126   llvm::sys::fs::remove_directories(m_root.GetPath());
127 }
128 
129 const FileSpec &Generator::GetRoot() const { return m_root; }
130 
131 void Generator::AddProvidersToIndex() {
132   FileSpec index = m_root;
133   index.AppendPathComponent("index.yaml");
134 
135   std::error_code EC;
136   auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC,
137                                                 sys::fs::OpenFlags::F_None);
138   yaml::Output yout(*strm);
139 
140   for (auto &provider : m_providers) {
141     auto &provider_info = provider.second->GetInfo();
142     yout << const_cast<ProviderInfo &>(provider_info);
143   }
144 }
145 
146 Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {}
147 
148 llvm::Error Loader::LoadIndex() {
149   if (m_loaded)
150     return llvm::Error::success();
151 
152   FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml");
153 
154   auto error_or_file = MemoryBuffer::getFile(index.GetPath());
155   if (auto err = error_or_file.getError())
156     return errorCodeToError(err);
157 
158   std::vector<ProviderInfo> provider_info;
159   yaml::Input yin((*error_or_file)->getBuffer());
160   yin >> provider_info;
161 
162   if (auto err = yin.error())
163     return errorCodeToError(err);
164 
165   for (auto &info : provider_info)
166     m_provider_info[info.name] = info;
167 
168   m_loaded = true;
169 
170   return llvm::Error::success();
171 }
172 
173 llvm::Optional<ProviderInfo> Loader::GetProviderInfo(StringRef name) {
174   assert(m_loaded);
175 
176   auto it = m_provider_info.find(name);
177   if (it == m_provider_info.end())
178     return llvm::None;
179 
180   return it->second;
181 }
182 
183 void ProviderBase::anchor() {}
184 char ProviderBase::ID = 0;
185