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 
25 namespace lldb_private {
26 namespace 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.
31 class AbstractRecorder {
32 protected:
AbstractRecorder(const FileSpec & filename,std::error_code & ec)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 
38 public:
GetFilename()39   const FileSpec &GetFilename() { return m_filename; }
40 
Stop()41   void Stop() {
42     assert(m_record);
43     m_record = false;
44   }
45 
46 private:
47   FileSpec m_filename;
48 
49 protected:
50   llvm::raw_fd_ostream m_os;
51   bool m_record;
52 };
53 
54 /// Recorder that records its data as text to a file.
55 class DataRecorder : public AbstractRecorder {
56 public:
DataRecorder(const FileSpec & filename,std::error_code & ec)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.
74 class YamlRecorder : public AbstractRecorder {
75 public:
YamlRecorder(const FileSpec & filename,std::error_code & ec)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 
Record(const T & t)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 
94 class FlushingFileCollector : public llvm::FileCollectorBase {
95 public:
96   FlushingFileCollector(llvm::StringRef files_path, llvm::StringRef dirs_path,
97                         std::error_code &ec);
98 
99 protected:
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 
111 class FileProvider : public Provider<FileProvider> {
112 public:
113   struct Info {
114     static const char *name;
115     static const char *file;
116   };
117 
FileProvider(const FileSpec & directory)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 
GetFileCollector()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 
136 private:
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.
144 class VersionProvider : public Provider<VersionProvider> {
145 public:
VersionProvider(const FileSpec & directory)146   VersionProvider(const FileSpec &directory) : Provider(directory) {}
147   struct Info {
148     static const char *name;
149     static const char *file;
150   };
SetVersion(std::string version)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.
161 template <typename T> class DirectoryProvider : public repro::Provider<T> {
162 public:
DirectoryProvider(const FileSpec & root)163   DirectoryProvider(const FileSpec &root) : Provider<T>(root) {}
SetDirectory(std::string directory)164   void SetDirectory(std::string directory) {
165     m_directory = std::move(directory);
166   }
GetDirectory()167   llvm::StringRef GetDirectory() { return m_directory; }
168 
Keep()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 
178 protected:
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.
186 class WorkingDirectoryProvider
187     : public DirectoryProvider<WorkingDirectoryProvider> {
188 public:
WorkingDirectoryProvider(const FileSpec & directory)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.
207 class HomeDirectoryProvider : public DirectoryProvider<HomeDirectoryProvider> {
208 public:
HomeDirectoryProvider(const FileSpec & directory)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.
223 class SymbolFileProvider : public Provider<SymbolFileProvider> {
224 public:
SymbolFileProvider(const FileSpec & directory)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;
EntryEntry234     Entry(std::string uuid) : uuid(std::move(uuid)) {}
EntryEntry235     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 
253 private:
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.
264 template <typename T, typename V>
265 class MultiProvider : public repro::Provider<V> {
266 public:
MultiProvider(const FileSpec & directory)267   MultiProvider(const FileSpec &directory) : Provider<V>(directory) {}
268 
GetNewRecorder()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 
Keep()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 
Discard()301   void Discard() override { m_recorders.clear(); }
302 
303 private:
304   std::vector<std::unique_ptr<T>> m_recorders;
305 };
306 
307 class CommandProvider : public MultiProvider<DataRecorder, CommandProvider> {
308 public:
309   struct Info {
310     static const char *name;
311     static const char *file;
312   };
313 
CommandProvider(const FileSpec & directory)314   CommandProvider(const FileSpec &directory)
315       : MultiProvider<DataRecorder, CommandProvider>(directory) {}
316 
317   static char ID;
318 };
319 
320 class ProcessInfoRecorder : public AbstractRecorder {
321 public:
ProcessInfoRecorder(const FileSpec & filename,std::error_code & ec)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 
331 class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
332 public:
333   struct Info {
334     static const char *name;
335     static const char *file;
336   };
337 
ProcessInfoProvider(const FileSpec & directory)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 
347 private:
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.
354 template <typename T> class MultiLoader {
355 public:
MultiLoader(std::vector<std::string> files)356   MultiLoader(std::vector<std::string> files) : m_files(std::move(files)) {}
357 
Create(Loader * loader)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 
GetNextFile()386   llvm::Optional<std::string> GetNextFile() {
387     if (m_index >= m_files.size())
388       return {};
389     return m_files[m_index++];
390   }
391 
392 private:
393   std::vector<std::string> m_files;
394   unsigned m_index = 0;
395 };
396 
397 class SymbolFileLoader {
398 public:
399   SymbolFileLoader(Loader *loader);
400   std::pair<FileSpec, FileSpec> GetPaths(const UUID *uuid) const;
401 
402 private:
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.
408 template <typename T>
GetDirectoryFrom(repro::Loader * loader)409 llvm::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 
LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry)419 LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry)
420 
421 namespace llvm {
422 namespace yaml {
423 template <>
424 struct 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