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