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