Skip to content

Commit 2b6162b

Browse files
committed
[lldb] Move copying of files into reproducer out of process
For performance reasons the reproducers don't copy the files captured by the file collector eagerly, but wait until the reproducer needs to be generated. This is a problematic when LLDB crashes and we have to do all this signal-unsafe work in the signal handler. This patch uses a similar trick to clang, which has the driver invoke a new cc1 instance to do all this work out-of-process. This patch moves the writing of the mapping file as well as copying over the reproducers into a separate process spawned when lldb crashes. Differential revision: https://reviews.llvm.org/D89600 (cherry picked from commit 73811d3)
1 parent 9fb02e4 commit 2b6162b

File tree

14 files changed

+217
-53
lines changed

14 files changed

+217
-53
lines changed

lldb/include/lldb/API/SBReproducer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class LLDB_API SBReproducer {
4848
static const char *Replay(const char *path, bool skip_version_check);
4949
static const char *Replay(const char *path, const SBReplayOptions &options);
5050
static const char *PassiveReplay(const char *path);
51+
static const char *Finalize(const char *path);
5152
static const char *GetPath();
5253
static bool SetAutoGenerate(bool b);
5354
static bool Generate();

lldb/include/lldb/Host/FileSystem.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class FileSystem {
3434
FileSystem()
3535
: m_fs(llvm::vfs::getRealFileSystem()), m_collector(nullptr),
3636
m_home_directory(), m_mapped(false) {}
37-
FileSystem(std::shared_ptr<llvm::FileCollector> collector)
37+
FileSystem(std::shared_ptr<llvm::FileCollectorBase> collector)
3838
: m_fs(llvm::vfs::getRealFileSystem()), m_collector(std::move(collector)),
3939
m_home_directory(), m_mapped(false) {}
4040
FileSystem(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs,
@@ -48,7 +48,7 @@ class FileSystem {
4848
static FileSystem &Instance();
4949

5050
static void Initialize();
51-
static void Initialize(std::shared_ptr<llvm::FileCollector> collector);
51+
static void Initialize(std::shared_ptr<llvm::FileCollectorBase> collector);
5252
static llvm::Error Initialize(const FileSpec &mapping);
5353
static void Initialize(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fs);
5454
static void Terminate();
@@ -199,7 +199,7 @@ class FileSystem {
199199
private:
200200
static llvm::Optional<FileSystem> &InstanceImpl();
201201
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> m_fs;
202-
std::shared_ptr<llvm::FileCollector> m_collector;
202+
std::shared_ptr<llvm::FileCollectorBase> m_collector;
203203
std::string m_home_directory;
204204
bool m_mapped;
205205
};

lldb/include/lldb/Utility/Reproducer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ struct ReplayOptions {
243243
bool check_version = true;
244244
};
245245

246+
llvm::Error Finalize(Loader *loader);
247+
llvm::Error Finalize(const FileSpec &root);
248+
246249
} // namespace repro
247250
} // namespace lldb_private
248251

lldb/include/lldb/Utility/ReproducerProvider.h

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -90,38 +90,50 @@ class YamlRecorder : public AbstractRecorder {
9090
}
9191
};
9292

93+
class FlushingFileCollector : public llvm::FileCollectorBase {
94+
public:
95+
FlushingFileCollector(llvm::StringRef files_path, llvm::StringRef dirs_path,
96+
std::error_code &ec);
97+
98+
protected:
99+
void addFileImpl(llvm::StringRef file) override;
100+
101+
llvm::vfs::directory_iterator
102+
addDirectoryImpl(const llvm::Twine &dir,
103+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
104+
std::error_code &dir_ec) override;
105+
106+
llvm::Optional<llvm::raw_fd_ostream> m_files_os;
107+
llvm::Optional<llvm::raw_fd_ostream> m_dirs_os;
108+
};
109+
93110
class FileProvider : public Provider<FileProvider> {
94111
public:
95112
struct Info {
96113
static const char *name;
97114
static const char *file;
98115
};
99116

100-
FileProvider(const FileSpec &directory)
101-
: Provider(directory),
102-
m_collector(std::make_shared<llvm::FileCollector>(
103-
directory.CopyByAppendingPathComponent("root").GetPath(),
104-
directory.GetPath())) {}
117+
FileProvider(const FileSpec &directory) : Provider(directory) {
118+
std::error_code ec;
119+
m_collector = std::make_shared<FlushingFileCollector>(
120+
directory.CopyByAppendingPathComponent("files.txt").GetPath(),
121+
directory.CopyByAppendingPathComponent("dirs.txt").GetPath(), ec);
122+
if (ec)
123+
m_collector.reset();
124+
}
105125

106-
std::shared_ptr<llvm::FileCollector> GetFileCollector() {
126+
std::shared_ptr<llvm::FileCollectorBase> GetFileCollector() {
107127
return m_collector;
108128
}
109129

110130
void RecordInterestingDirectory(const llvm::Twine &dir);
111131
void RecordInterestingDirectoryRecursive(const llvm::Twine &dir);
112132

113-
void Keep() override {
114-
auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
115-
// Temporary files that are removed during execution can cause copy errors.
116-
if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
117-
return;
118-
m_collector->writeMapping(mapping.GetPath());
119-
}
120-
121133
static char ID;
122134

123135
private:
124-
std::shared_ptr<llvm::FileCollector> m_collector;
136+
std::shared_ptr<FlushingFileCollector> m_collector;
125137
};
126138

127139
/// Provider for the LLDB version number.

lldb/source/API/SBReproducer.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include "SBReproducerPrivate.h"
1010

11-
#include "SBReproducerPrivate.h"
1211
#include "lldb/API/LLDB.h"
1312
#include "lldb/API/SBAddress.h"
1413
#include "lldb/API/SBAttachInfo.h"
@@ -266,6 +265,27 @@ const char *SBReproducer::Replay(const char *path,
266265
return nullptr;
267266
}
268267

268+
const char *SBReproducer::Finalize(const char *path) {
269+
static std::string error;
270+
if (auto e = Reproducer::Initialize(ReproducerMode::Replay, FileSpec(path))) {
271+
error = llvm::toString(std::move(e));
272+
return error.c_str();
273+
}
274+
275+
repro::Loader *loader = repro::Reproducer::Instance().GetLoader();
276+
if (!loader) {
277+
error = "unable to get replay loader.";
278+
return error.c_str();
279+
}
280+
281+
if (auto e = repro::Finalize(loader)) {
282+
error = llvm::toString(std::move(e));
283+
return error.c_str();
284+
}
285+
286+
return nullptr;
287+
}
288+
269289
bool SBReproducer::Generate() {
270290
auto &r = Reproducer::Instance();
271291
if (auto generator = r.GetGenerator()) {
@@ -285,10 +305,11 @@ bool SBReproducer::SetAutoGenerate(bool b) {
285305
}
286306

287307
const char *SBReproducer::GetPath() {
288-
static std::string path;
308+
ConstString path;
289309
auto &r = Reproducer::Instance();
290-
path = r.GetReproducerPath().GetCString();
291-
return path.c_str();
310+
if (FileSpec reproducer_path = Reproducer::Instance().GetReproducerPath())
311+
path = ConstString(r.GetReproducerPath().GetCString());
312+
return path.GetCString();
292313
}
293314

294315
void SBReproducer::SetWorkingDirectory(const char *path) {

lldb/source/Commands/CommandObjectReproducer.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ class CommandObjectReproducerGenerate : public CommandObjectParsed {
192192
auto &r = Reproducer::Instance();
193193
if (auto generator = r.GetGenerator()) {
194194
generator->Keep();
195+
if (llvm::Error e = repro::Finalize(r.GetReproducerPath())) {
196+
SetError(result, std::move(e));
197+
return result.Succeeded();
198+
}
195199
} else if (r.IsReplaying()) {
196200
// Make this operation a NO-OP in replay mode.
197201
result.SetStatus(eReturnStatusSuccessFinishNoResult);

lldb/source/Host/common/FileSystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ void FileSystem::Initialize() {
4949
InstanceImpl().emplace();
5050
}
5151

52-
void FileSystem::Initialize(std::shared_ptr<FileCollector> collector) {
52+
void FileSystem::Initialize(std::shared_ptr<FileCollectorBase> collector) {
5353
lldbassert(!InstanceImpl() && "Already initialized.");
5454
InstanceImpl().emplace(collector);
5555
}

lldb/source/Plugins/ExpressionParser/Clang/ModuleDependencyCollector.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ModuleDependencyCollectorAdaptor
1818
: public clang::ModuleDependencyCollector {
1919
public:
2020
ModuleDependencyCollectorAdaptor(
21-
std::shared_ptr<llvm::FileCollector> file_collector)
21+
std::shared_ptr<llvm::FileCollectorBase> file_collector)
2222
: clang::ModuleDependencyCollector(""), m_file_collector(file_collector) {
2323
}
2424

@@ -33,7 +33,7 @@ class ModuleDependencyCollectorAdaptor
3333
void writeFileMap() override {}
3434

3535
private:
36-
std::shared_ptr<llvm::FileCollector> m_file_collector;
36+
std::shared_ptr<llvm::FileCollectorBase> m_file_collector;
3737
};
3838
} // namespace lldb_private
3939

lldb/source/Utility/Reproducer.cpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,12 @@ Generator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) {
176176

177177
Generator::~Generator() {
178178
if (!m_done) {
179-
if (m_auto_generate)
179+
if (m_auto_generate) {
180180
Keep();
181-
else
181+
llvm::cantFail(Finalize(GetRoot()));
182+
} else {
182183
Discard();
184+
}
183185
}
184186
}
185187

@@ -359,3 +361,58 @@ void Verifier::Verify(
359361
}
360362
}
361363
}
364+
365+
static llvm::Error addPaths(StringRef path,
366+
function_ref<void(StringRef)> callback) {
367+
auto buffer = llvm::MemoryBuffer::getFile(path);
368+
if (!buffer)
369+
return errorCodeToError(buffer.getError());
370+
371+
SmallVector<StringRef, 0> paths;
372+
(*buffer)->getBuffer().split(paths, '\0');
373+
for (StringRef p : paths) {
374+
if (!p.empty())
375+
callback(p);
376+
}
377+
378+
return errorCodeToError(llvm::sys::fs::remove(path));
379+
}
380+
381+
llvm::Error repro::Finalize(Loader *loader) {
382+
if (!loader)
383+
return make_error<StringError>("invalid loader",
384+
llvm::inconvertibleErrorCode());
385+
386+
FileSpec reproducer_root = loader->GetRoot();
387+
std::string files_path =
388+
reproducer_root.CopyByAppendingPathComponent("files.txt").GetPath();
389+
std::string dirs_path =
390+
reproducer_root.CopyByAppendingPathComponent("dirs.txt").GetPath();
391+
392+
FileCollector collector(
393+
reproducer_root.CopyByAppendingPathComponent("root").GetPath(),
394+
reproducer_root.GetPath());
395+
396+
if (Error e =
397+
addPaths(files_path, [&](StringRef p) { collector.addFile(p); }))
398+
return e;
399+
400+
if (Error e =
401+
addPaths(dirs_path, [&](StringRef p) { collector.addDirectory(p); }))
402+
return e;
403+
404+
FileSpec mapping =
405+
reproducer_root.CopyByAppendingPathComponent(FileProvider::Info::file);
406+
if (auto ec = collector.copyFiles(/*stop_on_error=*/false))
407+
return errorCodeToError(ec);
408+
collector.writeMapping(mapping.GetPath());
409+
410+
return llvm::Error::success();
411+
}
412+
413+
llvm::Error repro::Finalize(const FileSpec &root) {
414+
Loader loader(root);
415+
if (Error e = loader.LoadIndex())
416+
return e;
417+
return Finalize(&loader);
418+
}

lldb/source/Utility/ReproducerProvider.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "lldb/Utility/ReproducerProvider.h"
1010
#include "lldb/Utility/ProcessInfo.h"
11+
#include "llvm/ADT/ScopeExit.h"
1112
#include "llvm/Support/FileSystem.h"
1213
#include "llvm/Support/WithColor.h"
1314
#include "llvm/Support/raw_ostream.h"
@@ -44,6 +45,40 @@ void VersionProvider::Keep() {
4445
os << m_version << "\n";
4546
}
4647

48+
FlushingFileCollector::FlushingFileCollector(llvm::StringRef files_path,
49+
llvm::StringRef dirs_path,
50+
std::error_code &ec) {
51+
auto clear = llvm::make_scope_exit([this]() {
52+
m_files_os.reset();
53+
m_dirs_os.reset();
54+
});
55+
m_files_os.emplace(files_path, ec, llvm::sys::fs::OF_Append);
56+
if (ec)
57+
return;
58+
m_dirs_os.emplace(dirs_path, ec, llvm::sys::fs::OF_Append);
59+
if (ec)
60+
return;
61+
clear.release();
62+
}
63+
64+
void FlushingFileCollector::addFileImpl(StringRef file) {
65+
if (m_files_os) {
66+
*m_files_os << file << '\0';
67+
m_files_os->flush();
68+
}
69+
}
70+
71+
llvm::vfs::directory_iterator
72+
FlushingFileCollector::addDirectoryImpl(const Twine &dir,
73+
IntrusiveRefCntPtr<vfs::FileSystem> vfs,
74+
std::error_code &dir_ec) {
75+
if (m_dirs_os) {
76+
*m_dirs_os << dir << '\0';
77+
m_dirs_os->flush();
78+
}
79+
return vfs->dir_begin(dir, dir_ec);
80+
}
81+
4782
void FileProvider::RecordInterestingDirectory(const llvm::Twine &dir) {
4883
if (m_collector)
4984
m_collector->addFile(dir);

lldb/test/Shell/Reproducer/TestCaptureEnvOverride.test

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
# RUN: LLDB_CAPTURE_REPRODUCER=0 %lldb -b -o 'reproducer status' --capture --capture-path %t.repro | FileCheck %s --check-prefix OFF
1111
# RUN: LLDB_CAPTURE_REPRODUCER=0 %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix OFF
12-
# RUN: LLDB_CAPTURE_REPRODUCER=OFF %lldb -b -o 'reproducer status' --capture --capture-path %t.repro | FileCheck %s --check-prefix OFF
13-
# RUN: LLDB_CAPTURE_REPRODUCER=off %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix OFF
1412

1513
# RUN: LLDB_CAPTURE_REPRODUCER=bogus %lldb -b -o 'reproducer status' --capture | FileCheck %s --check-prefix CAPTURE
1614
# RUN: LLDB_CAPTURE_REPRODUCER=bogus %lldb -b -o 'reproducer status' | FileCheck %s --check-prefix OFF
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# RUN: mkdir -p %t.repro
2+
# RUN: touch %t.known.file
3+
# RUN: mkdir -p %t.known.dir
4+
# RUN: touch %t.repro/index.yaml
5+
# RUN: echo -n "%t.known.file" > %t.repro/files.txt
6+
# RUN: echo -n "%t.known.dir" > %t.repro/dirs.txt
7+
8+
# RUN: %lldb --reproducer-finalize %t.repro 2>&1 | FileCheck %s
9+
# CHECK-NOT: error
10+
# CHECK: Reproducer written to
11+
12+
# RUN: echo "CHECK-DAG: %t.known.file" > %t.filecheck
13+
# RUN: echo "CHECK-DAG %t.known.dir" >> %t.filecheck
14+
# RUN: cat %t.repro/files.yaml | FileCheck %t.filecheck

0 commit comments

Comments
 (0)