1 //===-- Reproducer.cpp ----------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Utility/Reproducer.h"
10 #include "lldb/Utility/LLDBAssert.h"
11 #include "lldb/Utility/ReproducerProvider.h"
12 #include "lldb/Utility/Timer.h"
14 #include "llvm/Support/FileSystem.h"
15 #include "llvm/Support/Threading.h"
16 #include "llvm/Support/raw_ostream.h"
18 using namespace lldb_private
;
19 using namespace lldb_private::repro
;
21 using namespace llvm::yaml
;
23 Reproducer
&Reproducer::Instance() { return *InstanceImpl(); }
25 llvm::Error
Reproducer::Initialize(ReproducerMode mode
,
26 llvm::Optional
<FileSpec
> root
) {
27 lldbassert(!InstanceImpl() && "Already initialized.");
28 InstanceImpl().emplace();
31 case ReproducerMode::Capture
: {
33 SmallString
<128> repro_dir
;
34 auto ec
= sys::fs::createUniqueDirectory("reproducer", repro_dir
);
36 return make_error
<StringError
>(
37 "unable to create unique reproducer directory", ec
);
38 root
.emplace(repro_dir
);
40 auto ec
= sys::fs::create_directory(root
->GetPath());
42 return make_error
<StringError
>("unable to create reproducer directory",
45 return Instance().SetCapture(root
);
47 case ReproducerMode::Off
:
51 return Error::success();
54 void Reproducer::Initialize() {
55 llvm::cantFail(Initialize(repro::ReproducerMode::Off
, llvm::None
));
58 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); }
60 void Reproducer::Terminate() {
61 lldbassert(InstanceImpl() && "Already terminated.");
62 InstanceImpl().reset();
65 Optional
<Reproducer
> &Reproducer::InstanceImpl() {
66 static Optional
<Reproducer
> g_reproducer
;
70 const Generator
*Reproducer::GetGenerator() const {
71 std::lock_guard
<std::mutex
> guard(m_mutex
);
73 return &(*m_generator
);
77 const Loader
*Reproducer::GetLoader() const {
78 std::lock_guard
<std::mutex
> guard(m_mutex
);
84 Generator
*Reproducer::GetGenerator() {
85 std::lock_guard
<std::mutex
> guard(m_mutex
);
87 return &(*m_generator
);
91 Loader
*Reproducer::GetLoader() {
92 std::lock_guard
<std::mutex
> guard(m_mutex
);
98 llvm::Error
Reproducer::SetCapture(llvm::Optional
<FileSpec
> root
) {
99 std::lock_guard
<std::mutex
> guard(m_mutex
);
101 if (root
&& m_loader
)
102 return make_error
<StringError
>(
103 "cannot generate a reproducer when replay one",
104 inconvertibleErrorCode());
108 return Error::success();
111 m_generator
.emplace(*root
);
112 return Error::success();
115 FileSpec
Reproducer::GetReproducerPath() const {
116 if (auto g
= GetGenerator())
118 if (auto l
= GetLoader())
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());
130 Generator::Generator(FileSpec root
) : m_root(MakeAbsolute(std::move(root
))) {
131 GetOrCreate
<repro::WorkingDirectoryProvider
>();
132 GetOrCreate
<repro::HomeDirectoryProvider
>();
135 Generator::~Generator() {
137 if (m_auto_generate
) {
139 llvm::cantFail(Finalize(GetRoot()));
146 ProviderBase
*Generator::Register(std::unique_ptr
<ProviderBase
> provider
) {
147 std::lock_guard
<std::mutex
> lock(m_providers_mutex
);
148 std::pair
<const void *, std::unique_ptr
<ProviderBase
>> key_value(
149 provider
->DynamicClassID(), std::move(provider
));
150 auto e
= m_providers
.insert(std::move(key_value
));
151 return e
.first
->getSecond().get();
154 void Generator::Keep() {
159 for (auto &provider
: m_providers
)
160 provider
.second
->Keep();
162 AddProvidersToIndex();
165 void Generator::Discard() {
170 for (auto &provider
: m_providers
)
171 provider
.second
->Discard();
173 llvm::sys::fs::remove_directories(m_root
.GetPath());
176 void Generator::SetAutoGenerate(bool b
) { m_auto_generate
= b
; }
178 bool Generator::IsAutoGenerate() const { return m_auto_generate
; }
180 const FileSpec
&Generator::GetRoot() const { return m_root
; }
182 void Generator::AddProvidersToIndex() {
183 FileSpec index
= m_root
;
184 index
.AppendPathComponent("index.yaml");
187 auto strm
= std::make_unique
<raw_fd_ostream
>(index
.GetPath(), EC
,
188 sys::fs::OpenFlags::OF_None
);
189 yaml::Output
yout(*strm
);
191 std::vector
<std::string
> files
;
192 files
.reserve(m_providers
.size());
193 for (auto &provider
: m_providers
) {
194 files
.emplace_back(provider
.second
->GetFile());
200 Loader::Loader(FileSpec root
, bool passive
)
201 : m_root(MakeAbsolute(std::move(root
))), m_loaded(false) {}
203 llvm::Error
Loader::LoadIndex() {
205 return llvm::Error::success();
207 FileSpec index
= m_root
.CopyByAppendingPathComponent("index.yaml");
209 auto error_or_file
= MemoryBuffer::getFile(index
.GetPath());
210 if (auto err
= error_or_file
.getError())
211 return make_error
<StringError
>("unable to load reproducer index", err
);
213 yaml::Input
yin((*error_or_file
)->getBuffer());
215 if (auto err
= yin
.error())
216 return make_error
<StringError
>("unable to read reproducer index", err
);
218 // Sort files to speed up search.
221 // Remember that we've loaded the index.
224 return llvm::Error::success();
227 bool Loader::HasFile(StringRef file
) {
229 auto it
= std::lower_bound(m_files
.begin(), m_files
.end(), file
.str());
230 return (it
!= m_files
.end()) && (*it
== file
);
233 void Verifier::Verify(
234 llvm::function_ref
<void(llvm::StringRef
)> error_callback
,
235 llvm::function_ref
<void(llvm::StringRef
)> warning_callback
,
236 llvm::function_ref
<void(llvm::StringRef
)> note_callack
) const {
238 error_callback("invalid loader");
242 FileSpec vfs_mapping
= m_loader
->GetFile
<FileProvider::Info
>();
243 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> buffer
=
244 vfs::getRealFileSystem()->getBufferForFile(vfs_mapping
.GetPath());
246 error_callback("unable to read files: " + buffer
.getError().message());
250 IntrusiveRefCntPtr
<vfs::FileSystem
> vfs
= vfs::getVFSFromYAML(
251 std::move(buffer
.get()), nullptr, vfs_mapping
.GetPath());
253 error_callback("unable to initialize the virtual file system");
257 auto &redirecting_vfs
= static_cast<vfs::RedirectingFileSystem
&>(*vfs
);
258 redirecting_vfs
.setFallthrough(false);
261 llvm::Expected
<std::string
> working_dir
=
262 GetDirectoryFrom
<WorkingDirectoryProvider
>(m_loader
);
264 if (!vfs
->exists(*working_dir
))
265 warning_callback("working directory '" + *working_dir
+ "' not in VFS");
266 vfs
->setCurrentWorkingDirectory(*working_dir
);
268 warning_callback("no working directory in reproducer: " +
269 toString(working_dir
.takeError()));
274 llvm::Expected
<std::string
> home_dir
=
275 GetDirectoryFrom
<HomeDirectoryProvider
>(m_loader
);
277 if (!vfs
->exists(*home_dir
))
278 warning_callback("home directory '" + *home_dir
+ "' not in VFS");
280 warning_callback("no home directory in reproducer: " +
281 toString(home_dir
.takeError()));
286 Expected
<std::string
> symbol_files
=
287 m_loader
->LoadBuffer
<SymbolFileProvider
>();
289 std::vector
<SymbolFileProvider::Entry
> entries
;
290 llvm::yaml::Input
yin(*symbol_files
);
292 for (const auto &entry
: entries
) {
293 if (!entry
.module_path
.empty() && !vfs
->exists(entry
.module_path
)) {
294 warning_callback("'" + entry
.module_path
+ "': module path for " +
295 entry
.uuid
+ " not in VFS");
297 if (!entry
.symbol_path
.empty() && !vfs
->exists(entry
.symbol_path
)) {
298 warning_callback("'" + entry
.symbol_path
+ "': symbol path for " +
299 entry
.uuid
+ " not in VFS");
303 llvm::consumeError(symbol_files
.takeError());
307 // Missing files in the VFS are notes rather than warnings. Because the VFS
308 // is a snapshot, temporary files could have been removed between when they
309 // were recorded and when the reproducer was generated.
310 std::vector
<llvm::StringRef
> roots
= redirecting_vfs
.getRoots();
311 for (llvm::StringRef root
: roots
) {
313 vfs::recursive_directory_iterator
iter(*vfs
, root
, ec
);
314 vfs::recursive_directory_iterator end
;
315 for (; iter
!= end
&& !ec
; iter
.increment(ec
)) {
316 ErrorOr
<vfs::Status
> status
= vfs
->status(iter
->path());
318 note_callack("'" + iter
->path().str() +
319 "': " + status
.getError().message());
324 static llvm::Error
addPaths(StringRef path
,
325 function_ref
<void(StringRef
)> callback
) {
326 auto buffer
= llvm::MemoryBuffer::getFile(path
);
328 return errorCodeToError(buffer
.getError());
330 SmallVector
<StringRef
, 0> paths
;
331 (*buffer
)->getBuffer().split(paths
, '\0');
332 for (StringRef p
: paths
) {
333 if (!p
.empty() && llvm::sys::fs::exists(p
))
337 return errorCodeToError(llvm::sys::fs::remove(path
));
340 llvm::Error
repro::Finalize(Loader
*loader
) {
342 return make_error
<StringError
>("invalid loader",
343 llvm::inconvertibleErrorCode());
345 FileSpec reproducer_root
= loader
->GetRoot();
346 std::string files_path
=
347 reproducer_root
.CopyByAppendingPathComponent("files.txt").GetPath();
348 std::string dirs_path
=
349 reproducer_root
.CopyByAppendingPathComponent("dirs.txt").GetPath();
351 FileCollector
collector(
352 reproducer_root
.CopyByAppendingPathComponent("root").GetPath(),
353 reproducer_root
.GetPath());
356 addPaths(files_path
, [&](StringRef p
) { collector
.addFile(p
); }))
360 addPaths(dirs_path
, [&](StringRef p
) { collector
.addDirectory(p
); }))
364 reproducer_root
.CopyByAppendingPathComponent(FileProvider::Info::file
);
365 if (auto ec
= collector
.copyFiles(/*StopOnError=*/false))
366 return errorCodeToError(ec
);
367 collector
.writeMapping(mapping
.GetPath());
369 return llvm::Error::success();
372 llvm::Error
repro::Finalize(const FileSpec
&root
) {
374 if (Error e
= loader
.LoadIndex())
376 return Finalize(&loader
);