1//===-- Reproducer.h --------------------------------------------*- C++ -*-===//
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#ifndef LLDB_UTILITY_REPRODUCER_PROVIDER_H
10#define LLDB_UTILITY_REPRODUCER_PROVIDER_H
11
12#include "lldb/Utility/FileSpec.h"
13#include "lldb/Utility/ProcessInfo.h"
14#include "lldb/Utility/Reproducer.h"
15#include "lldb/Utility/UUID.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Support/Error.h"
18#include "llvm/Support/FileCollector.h"
19#include "llvm/Support/YAMLTraits.h"
20
21#include <string>
22#include <utility>
23#include <vector>
24
25namespace lldb_private {
26namespace repro {
27
28/// The recorder is a small object handed out by a provider to record data. It
29/// is commonly used in combination with a MultiProvider which is meant to
30/// record information for multiple instances of the same source of data.
31class AbstractRecorder {
32protected:
33 AbstractRecorder(const FileSpec &filename, std::error_code &ec)
34 : m_filename(filename.GetFilename().GetStringRef()),
35 m_os(filename.GetPath(), ec, llvm::sys::fs::OF_TextWithCRLF),
36 m_record(true) {}
37
38public:
39 const FileSpec &GetFilename() { return m_filename; }
40
41 void Stop() {
42 assert(m_record);
43 m_record = false;
44 }
45
46private:
47 FileSpec m_filename;
48
49protected:
50 llvm::raw_fd_ostream m_os;
51 bool m_record;
52};
53
54/// Recorder that records its data as text to a file.
55class DataRecorder : public AbstractRecorder {
56public:
57 DataRecorder(const FileSpec &filename, std::error_code &ec)
58 : AbstractRecorder(filename, ec) {}
59
60 static llvm::Expected<std::unique_ptr<DataRecorder>>
61 Create(const FileSpec &filename);
62
63 template <typename T> void Record(const T &t, bool newline = false) {
64 if (!m_record)
65 return;
66 m_os << t;
67 if (newline)
68 m_os << '\n';
69 m_os.flush();
70 }
71};
72
73/// Recorder that records its data as YAML to a file.
74class YamlRecorder : public AbstractRecorder {
75public:
76 YamlRecorder(const FileSpec &filename, std::error_code &ec)
77 : AbstractRecorder(filename, ec) {}
78
79 static llvm::Expected<std::unique_ptr<YamlRecorder>>
80 Create(const FileSpec &filename);
81
82 template <typename T> void Record(const T &t) {
83 if (!m_record)
84 return;
85 llvm::yaml::Output yout(m_os);
86 // The YAML traits are defined as non-const because they are used for
87 // serialization and deserialization. The cast is safe because
88 // serialization doesn't modify the object.
89 yout << const_cast<T &>(t);
90 m_os.flush();
91 }
92};
93
94class FlushingFileCollector : public llvm::FileCollectorBase {
95public:
96 FlushingFileCollector(llvm::StringRef files_path, llvm::StringRef dirs_path,
97 std::error_code &ec);
98
99protected:
100 void addFileImpl(llvm::StringRef file) override;
101
102 llvm::vfs::directory_iterator
103 addDirectoryImpl(const llvm::Twine &dir,
104 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs,
105 std::error_code &dir_ec) override;
106
107 llvm::Optional<llvm::raw_fd_ostream> m_files_os;
108 llvm::Optional<llvm::raw_fd_ostream> m_dirs_os;
109};
110
111class FileProvider : public Provider<FileProvider> {
112public:
113 struct Info {
114 static const char *name;
115 static const char *file;
116 };
117
118 FileProvider(const FileSpec &directory) : Provider(directory) {
119 std::error_code ec;
120 m_collector = std::make_shared<FlushingFileCollector>(
121 directory.CopyByAppendingPathComponent("files.txt").GetPath(),
122 directory.CopyByAppendingPathComponent("dirs.txt").GetPath(), ec);
123 if (ec)
124 m_collector.reset();
125 }
126
127 std::shared_ptr<llvm::FileCollectorBase> GetFileCollector() {
128 return m_collector;
129 }
130
131 void RecordInterestingDirectory(const llvm::Twine &dir);
132 void RecordInterestingDirectoryRecursive(const llvm::Twine &dir);
133
134 static char ID;
135
136private:
137 std::shared_ptr<FlushingFileCollector> m_collector;
138};
139
140/// Provider for the LLDB version number.
141///
142/// When the reproducer is kept, it writes the lldb version to a file named
143/// version.txt in the reproducer root.
144class VersionProvider : public Provider<VersionProvider> {
145public:
146 VersionProvider(const FileSpec &directory) : Provider(directory) {}
147 struct Info {
148 static const char *name;
149 static const char *file;
150 };
151 void SetVersion(std::string version) {
152 assert(m_version.empty());
153 m_version = std::move(version);
154 }
155 void Keep() override;
156 std::string m_version;
157 static char ID;
158};
159
160/// Abstract provider to storing directory paths.
161template <typename T> class DirectoryProvider : public repro::Provider<T> {
162public:
163 DirectoryProvider(const FileSpec &root) : Provider<T>(root) {}
164 void SetDirectory(std::string directory) {
165 m_directory = std::move(directory);
166 }
167 llvm::StringRef GetDirectory() { return m_directory; }
168
169 void Keep() override {
170 FileSpec file = this->GetRoot().CopyByAppendingPathComponent(T::Info::file);
171 std::error_code ec;
172 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_TextWithCRLF);
173 if (ec)
174 return;
175 os << m_directory << "\n";
176 }
177
178protected:
179 std::string m_directory;
180};
181
182/// Provider for the current working directory.
183///
184/// When the reproducer is kept, it writes lldb's current working directory to
185/// a file named cwd.txt in the reproducer root.
186class WorkingDirectoryProvider
187 : public DirectoryProvider<WorkingDirectoryProvider> {
188public:
189 WorkingDirectoryProvider(const FileSpec &directory)
190 : DirectoryProvider(directory) {
191 llvm::SmallString<128> cwd;
192 if (std::error_code EC = llvm::sys::fs::current_path(cwd))
193 return;
194 SetDirectory(std::string(cwd));
195 }
196 struct Info {
197 static const char *name;
198 static const char *file;
199 };
200 static char ID;
201};
202
203/// Provider for the home directory.
204///
205/// When the reproducer is kept, it writes the user's home directory to a file
206/// a file named home.txt in the reproducer root.
207class HomeDirectoryProvider : public DirectoryProvider<HomeDirectoryProvider> {
208public:
209 HomeDirectoryProvider(const FileSpec &directory)
210 : DirectoryProvider(directory) {
211 llvm::SmallString<128> home_dir;
212 llvm::sys::path::home_directory(home_dir);
213 SetDirectory(std::string(home_dir));
214 }
215 struct Info {
216 static const char *name;
217 static const char *file;
218 };
219 static char ID;
220};
221
222/// Provider for mapping UUIDs to symbol and executable files.
223class SymbolFileProvider : public Provider<SymbolFileProvider> {
224public:
225 SymbolFileProvider(const FileSpec &directory)
226 : Provider(directory), m_symbol_files() {}
227
228 void AddSymbolFile(const UUID *uuid, const FileSpec &module_path,
229 const FileSpec &symbol_path);
230 void Keep() override;
231
232 struct Entry {
233 Entry() = default;
234 Entry(std::string uuid) : uuid(std::move(uuid)) {}
235 Entry(std::string uuid, std::string module_path, std::string symbol_path)
236 : uuid(std::move(uuid)), module_path(std::move(module_path)),
237 symbol_path(std::move(symbol_path)) {}
238
239 bool operator==(const Entry &rhs) const { return uuid == rhs.uuid; }
240 bool operator<(const Entry &rhs) const { return uuid < rhs.uuid; }
241
242 std::string uuid;
243 std::string module_path;
244 std::string symbol_path;
245 };
246
247 struct Info {
248 static const char *name;
249 static const char *file;
250 };
251 static char ID;
252
253private:
254 std::vector<Entry> m_symbol_files;
255};
256
257/// The MultiProvider is a provider that hands out recorder which can be used
258/// to capture data for different instances of the same object. The recorders
259/// can be passed around or stored as an instance member.
260///
261/// The Info::file for the MultiProvider contains an index of files for every
262/// recorder. Use the MultiLoader to read the index and get the individual
263/// files.
264template <typename T, typename V>
265class MultiProvider : public repro::Provider<V> {
266public:
267 MultiProvider(const FileSpec &directory) : Provider<V>(directory) {}
268
269 T *GetNewRecorder() {
270 std::size_t i = m_recorders.size() + 1;
271 std::string filename = (llvm::Twine(V::Info::name) + llvm::Twine("-") +
272 llvm::Twine(i) + llvm::Twine(".yaml"))
273 .str();
274 auto recorder_or_error =
275 T::Create(this->GetRoot().CopyByAppendingPathComponent(filename));
276 if (!recorder_or_error) {
277 llvm::consumeError(recorder_or_error.takeError());
278 return nullptr;
279 }
280
281 m_recorders.push_back(std::move(*recorder_or_error));
282 return m_recorders.back().get();
283 }
284
285 void Keep() override {
286 std::vector<std::string> files;
287 for (auto &recorder : m_recorders) {
288 recorder->Stop();
289 files.push_back(recorder->GetFilename().GetPath());
290 }
291
292 FileSpec file = this->GetRoot().CopyByAppendingPathComponent(V::Info::file);
293 std::error_code ec;
294 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_TextWithCRLF);
295 if (ec)
296 return;
297 llvm::yaml::Output yout(os);
298 yout << files;
299 }
300
301 void Discard() override { m_recorders.clear(); }
302
303private:
304 std::vector<std::unique_ptr<T>> m_recorders;
305};
306
307class CommandProvider : public MultiProvider<DataRecorder, CommandProvider> {
308public:
309 struct Info {
310 static const char *name;
311 static const char *file;
312 };
313
314 CommandProvider(const FileSpec &directory)
315 : MultiProvider<DataRecorder, CommandProvider>(directory) {}
316
317 static char ID;
318};
319
320class ProcessInfoRecorder : public AbstractRecorder {
321public:
322 ProcessInfoRecorder(const FileSpec &filename, std::error_code &ec)
323 : AbstractRecorder(filename, ec) {}
324
325 static llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
326 Create(const FileSpec &filename);
327
328 void Record(const ProcessInstanceInfoList &process_infos);
329};
330
331class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
332public:
333 struct Info {
334 static const char *name;
335 static const char *file;
336 };
337
338 ProcessInfoProvider(const FileSpec &directory) : Provider(directory) {}
339
340 ProcessInfoRecorder *GetNewProcessInfoRecorder();
341
342 void Keep() override;
343 void Discard() override;
344
345 static char ID;
346
347private:
348 std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
349 std::vector<std::unique_ptr<ProcessInfoRecorder>> m_process_info_recorders;
350};
351
352/// Loader for data captured with the MultiProvider. It will read the index and
353/// return the path to the files in the index.
354template <typename T> class MultiLoader {
355public:
356 MultiLoader(std::vector<std::string> files) : m_files(std::move(files)) {}
357
358 static std::unique_ptr<MultiLoader> Create(Loader *loader) {
359 if (!loader)
360 return {};
361
362 FileSpec file = loader->GetFile<typename T::Info>();
363 if (!file)
364 return {};
365
366 auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
367 if (auto err = error_or_file.getError())
368 return {};
369
370 std::vector<std::string> files;
371 llvm::yaml::Input yin((*error_or_file)->getBuffer());
372 yin >> files;
373
374 if (auto err = yin.error())
375 return {};
376
377 for (auto &file : files) {
378 FileSpec absolute_path =
379 loader->GetRoot().CopyByAppendingPathComponent(file);
380 file = absolute_path.GetPath();
381 }
382
383 return std::make_unique<MultiLoader<T>>(std::move(files));
384 }
385
386 llvm::Optional<std::string> GetNextFile() {
387 if (m_index >= m_files.size())
388 return {};
389 return m_files[m_index++];
390 }
391
392private:
393 std::vector<std::string> m_files;
394 unsigned m_index = 0;
395};
396
397class SymbolFileLoader {
398public:
399 SymbolFileLoader(Loader *loader);
400 std::pair<FileSpec, FileSpec> GetPaths(const UUID *uuid) const;
401
402private:
403 // Sorted list of UUID to path mappings.
404 std::vector<SymbolFileProvider::Entry> m_symbol_files;
405};
406
407/// Helper to read directories written by the DirectoryProvider.
408template <typename T>
409llvm::Expected<std::string> GetDirectoryFrom(repro::Loader *loader) {
410 llvm::Expected<std::string> dir = loader->LoadBuffer<T>();
411 if (!dir)
412 return dir.takeError();
413 return std::string(llvm::StringRef(*dir).rtrim());
414}
415
416} // namespace repro
417} // namespace lldb_private
418
419LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry)
420
421namespace llvm {
422namespace yaml {
423template <>
424struct MappingTraits<lldb_private::repro::SymbolFileProvider::Entry> {
425 static void mapping(IO &io,
426 lldb_private::repro::SymbolFileProvider::Entry &entry) {
427 io.mapRequired("uuid", entry.uuid);
428 io.mapRequired("module-path", entry.module_path);
429 io.mapRequired("symbol-path", entry.symbol_path);
430 }
431};
432} // namespace yaml
433} // namespace llvm
434
435#endif // LLDB_UTILITY_REPRODUCER_PROVIDER_H
436