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 FileProvider : public Provider<FileProvider> {
95 public:
96   struct Info {
97     static const char *name;
98     static const char *file;
99   };
100 
FileProvider(const FileSpec & directory)101   FileProvider(const FileSpec &directory) : Provider(directory) {
102     m_collector = std::make_shared<llvm::FileCollector>(
103         directory.CopyByAppendingPathComponent("root").GetPath(),
104         directory.GetPath());
105   }
106 
GetFileCollector()107   std::shared_ptr<llvm::FileCollector> GetFileCollector() {
108     return m_collector;
109   }
110 
111   void Keep() override;
112 
113   void RecordInterestingDirectory(const llvm::Twine &dir);
114   void RecordInterestingDirectoryRecursive(const llvm::Twine &dir);
115 
116   static char ID;
117 
118 private:
119   std::shared_ptr<llvm::FileCollector> m_collector;
120 };
121 
122 /// Provider for the LLDB version number.
123 ///
124 /// When the reproducer is kept, it writes the lldb version to a file named
125 /// version.txt in the reproducer root.
126 class VersionProvider : public Provider<VersionProvider> {
127 public:
VersionProvider(const FileSpec & directory)128   VersionProvider(const FileSpec &directory) : Provider(directory) {}
129   struct Info {
130     static const char *name;
131     static const char *file;
132   };
SetVersion(std::string version)133   void SetVersion(std::string version) {
134     assert(m_version.empty());
135     m_version = std::move(version);
136   }
137   void Keep() override;
138   std::string m_version;
139   static char ID;
140 };
141 
142 /// Abstract provider to storing directory paths.
143 template <typename T> class DirectoryProvider : public repro::Provider<T> {
144 public:
DirectoryProvider(const FileSpec & root)145   DirectoryProvider(const FileSpec &root) : Provider<T>(root) {}
SetDirectory(std::string directory)146   void SetDirectory(std::string directory) {
147     m_directory = std::move(directory);
148   }
GetDirectory()149   llvm::StringRef GetDirectory() { return m_directory; }
150 
Keep()151   void Keep() override {
152     FileSpec file = this->GetRoot().CopyByAppendingPathComponent(T::Info::file);
153     std::error_code ec;
154     llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_TextWithCRLF);
155     if (ec)
156       return;
157     os << m_directory << "\n";
158   }
159 
160 protected:
161   std::string m_directory;
162 };
163 
164 /// Provider for the current working directory.
165 ///
166 /// When the reproducer is kept, it writes lldb's current working directory to
167 /// a file named cwd.txt in the reproducer root.
168 class WorkingDirectoryProvider
169     : public DirectoryProvider<WorkingDirectoryProvider> {
170 public:
WorkingDirectoryProvider(const FileSpec & directory)171   WorkingDirectoryProvider(const FileSpec &directory)
172       : DirectoryProvider(directory) {
173     llvm::SmallString<128> cwd;
174     if (std::error_code EC = llvm::sys::fs::current_path(cwd))
175       return;
176     SetDirectory(std::string(cwd));
177   }
178   struct Info {
179     static const char *name;
180     static const char *file;
181   };
182   static char ID;
183 };
184 
185 /// Provider for the home directory.
186 ///
187 /// When the reproducer is kept, it writes the user's home directory to a file
188 /// a file named home.txt in the reproducer root.
189 class HomeDirectoryProvider : public DirectoryProvider<HomeDirectoryProvider> {
190 public:
HomeDirectoryProvider(const FileSpec & directory)191   HomeDirectoryProvider(const FileSpec &directory)
192       : DirectoryProvider(directory) {
193     llvm::SmallString<128> home_dir;
194     llvm::sys::path::home_directory(home_dir);
195     SetDirectory(std::string(home_dir));
196   }
197   struct Info {
198     static const char *name;
199     static const char *file;
200   };
201   static char ID;
202 };
203 
204 /// Provider for mapping UUIDs to symbol and executable files.
205 class SymbolFileProvider : public Provider<SymbolFileProvider> {
206 public:
SymbolFileProvider(const FileSpec & directory)207   SymbolFileProvider(const FileSpec &directory) : Provider(directory) {}
208 
209   void AddSymbolFile(const UUID *uuid, const FileSpec &module_path,
210                      const FileSpec &symbol_path);
211   void Keep() override;
212 
213   struct Entry {
214     Entry() = default;
EntryEntry215     Entry(std::string uuid) : uuid(std::move(uuid)) {}
EntryEntry216     Entry(std::string uuid, std::string module_path, std::string symbol_path)
217         : uuid(std::move(uuid)), module_path(std::move(module_path)),
218           symbol_path(std::move(symbol_path)) {}
219 
220     bool operator==(const Entry &rhs) const { return uuid == rhs.uuid; }
221     bool operator<(const Entry &rhs) const { return uuid < rhs.uuid; }
222 
223     std::string uuid;
224     std::string module_path;
225     std::string symbol_path;
226   };
227 
228   struct Info {
229     static const char *name;
230     static const char *file;
231   };
232   static char ID;
233 
234 private:
235   std::vector<Entry> m_symbol_files;
236 };
237 
238 /// The MultiProvider is a provider that hands out recorder which can be used
239 /// to capture data for different instances of the same object. The recorders
240 /// can be passed around or stored as an instance member.
241 ///
242 /// The Info::file for the MultiProvider contains an index of files for every
243 /// recorder. Use the MultiLoader to read the index and get the individual
244 /// files.
245 template <typename T, typename V>
246 class MultiProvider : public repro::Provider<V> {
247 public:
MultiProvider(const FileSpec & directory)248   MultiProvider(const FileSpec &directory) : Provider<V>(directory) {}
249 
GetNewRecorder()250   T *GetNewRecorder() {
251     std::size_t i = m_recorders.size() + 1;
252     std::string filename = (llvm::Twine(V::Info::name) + llvm::Twine("-") +
253                             llvm::Twine(i) + llvm::Twine(".yaml"))
254                                .str();
255     auto recorder_or_error =
256         T::Create(this->GetRoot().CopyByAppendingPathComponent(filename));
257     if (!recorder_or_error) {
258       llvm::consumeError(recorder_or_error.takeError());
259       return nullptr;
260     }
261 
262     m_recorders.push_back(std::move(*recorder_or_error));
263     return m_recorders.back().get();
264   }
265 
Keep()266   void Keep() override {
267     std::vector<std::string> files;
268     for (auto &recorder : m_recorders) {
269       recorder->Stop();
270       files.push_back(recorder->GetFilename().GetPath());
271     }
272 
273     FileSpec file = this->GetRoot().CopyByAppendingPathComponent(V::Info::file);
274     std::error_code ec;
275     llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_TextWithCRLF);
276     if (ec)
277       return;
278     llvm::yaml::Output yout(os);
279     yout << files;
280   }
281 
Discard()282   void Discard() override { m_recorders.clear(); }
283 
284 private:
285   std::vector<std::unique_ptr<T>> m_recorders;
286 };
287 
288 class CommandProvider : public MultiProvider<DataRecorder, CommandProvider> {
289 public:
290   struct Info {
291     static const char *name;
292     static const char *file;
293   };
294 
CommandProvider(const FileSpec & directory)295   CommandProvider(const FileSpec &directory)
296       : MultiProvider<DataRecorder, CommandProvider>(directory) {}
297 
298   static char ID;
299 };
300 
301 class ProcessInfoRecorder : public AbstractRecorder {
302 public:
ProcessInfoRecorder(const FileSpec & filename,std::error_code & ec)303   ProcessInfoRecorder(const FileSpec &filename, std::error_code &ec)
304       : AbstractRecorder(filename, ec) {}
305 
306   static llvm::Expected<std::unique_ptr<ProcessInfoRecorder>>
307   Create(const FileSpec &filename);
308 
309   void Record(const ProcessInstanceInfoList &process_infos);
310 };
311 
312 class ProcessInfoProvider : public repro::Provider<ProcessInfoProvider> {
313 public:
314   struct Info {
315     static const char *name;
316     static const char *file;
317   };
318 
ProcessInfoProvider(const FileSpec & directory)319   ProcessInfoProvider(const FileSpec &directory) : Provider(directory) {}
320 
321   ProcessInfoRecorder *GetNewProcessInfoRecorder();
322 
323   void Keep() override;
324   void Discard() override;
325 
326   static char ID;
327 
328 private:
329   std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
330   std::vector<std::unique_ptr<ProcessInfoRecorder>> m_process_info_recorders;
331 };
332 
333 /// Loader for data captured with the MultiProvider. It will read the index and
334 /// return the path to the files in the index.
335 template <typename T> class MultiLoader {
336 public:
MultiLoader(std::vector<std::string> files)337   MultiLoader(std::vector<std::string> files) : m_files(std::move(files)) {}
338 
Create(Loader * loader)339   static std::unique_ptr<MultiLoader> Create(Loader *loader) {
340     if (!loader)
341       return {};
342 
343     FileSpec file = loader->GetFile<typename T::Info>();
344     if (!file)
345       return {};
346 
347     auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
348     if (auto err = error_or_file.getError())
349       return {};
350 
351     std::vector<std::string> files;
352     llvm::yaml::Input yin((*error_or_file)->getBuffer());
353     yin >> files;
354 
355     if (auto err = yin.error())
356       return {};
357 
358     for (auto &file : files) {
359       FileSpec absolute_path =
360           loader->GetRoot().CopyByAppendingPathComponent(file);
361       file = absolute_path.GetPath();
362     }
363 
364     return std::make_unique<MultiLoader<T>>(std::move(files));
365   }
366 
GetNextFile()367   llvm::Optional<std::string> GetNextFile() {
368     if (m_index >= m_files.size())
369       return {};
370     return m_files[m_index++];
371   }
372 
373 private:
374   std::vector<std::string> m_files;
375   unsigned m_index = 0;
376 };
377 
378 class SymbolFileLoader {
379 public:
380   SymbolFileLoader(Loader *loader);
381   std::pair<FileSpec, FileSpec> GetPaths(const UUID *uuid) const;
382 
383 private:
384   // Sorted list of UUID to path mappings.
385   std::vector<SymbolFileProvider::Entry> m_symbol_files;
386 };
387 
388 /// Helper to read directories written by the DirectoryProvider.
389 template <typename T>
GetDirectoryFrom(repro::Loader * loader)390 llvm::Expected<std::string> GetDirectoryFrom(repro::Loader *loader) {
391   llvm::Expected<std::string> dir = loader->LoadBuffer<T>();
392   if (!dir)
393     return dir.takeError();
394   return std::string(llvm::StringRef(*dir).rtrim());
395 }
396 
397 } // namespace repro
398 } // namespace lldb_private
399 
LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry)400 LLVM_YAML_IS_SEQUENCE_VECTOR(lldb_private::repro::SymbolFileProvider::Entry)
401 
402 namespace llvm {
403 namespace yaml {
404 template <>
405 struct MappingTraits<lldb_private::repro::SymbolFileProvider::Entry> {
406   static void mapping(IO &io,
407                       lldb_private::repro::SymbolFileProvider::Entry &entry) {
408     io.mapRequired("uuid", entry.uuid);
409     io.mapRequired("module-path", entry.module_path);
410     io.mapRequired("symbol-path", entry.symbol_path);
411   }
412 };
413 } // namespace yaml
414 } // namespace llvm
415 
416 #endif // LLDB_UTILITY_REPRODUCER_PROVIDER_H
417