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