]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/lldb/include/lldb/Utility/Reproducer.h
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / lldb / include / lldb / Utility / Reproducer.h
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_H
10 #define LLDB_UTILITY_REPRODUCER_H
11
12 #include "lldb/Utility/FileSpec.h"
13 #include "llvm/ADT/DenseMap.h"
14 #include "llvm/Support/Error.h"
15 #include "llvm/Support/FileCollector.h"
16 #include "llvm/Support/YAMLTraits.h"
17
18 #include <mutex>
19 #include <string>
20 #include <vector>
21
22 namespace lldb_private {
23 namespace repro {
24
25 class Reproducer;
26
27 enum class ReproducerMode {
28   Capture,
29   Replay,
30   Off,
31 };
32
33 /// The provider defines an interface for generating files needed for
34 /// reproducing.
35 ///
36 /// Different components will implement different providers.
37 class ProviderBase {
38 public:
39   virtual ~ProviderBase() = default;
40
41   const FileSpec &GetRoot() const { return m_root; }
42
43   /// The Keep method is called when it is decided that we need to keep the
44   /// data in order to provide a reproducer.
45   virtual void Keep(){};
46
47   /// The Discard method is called when it is decided that we do not need to
48   /// keep any information and will not generate a reproducer.
49   virtual void Discard(){};
50
51   // Returns the class ID for this type.
52   static const void *ClassID() { return &ID; }
53
54   // Returns the class ID for the dynamic type of this Provider instance.
55   virtual const void *DynamicClassID() const = 0;
56
57   virtual llvm::StringRef GetName() const = 0;
58   virtual llvm::StringRef GetFile() const = 0;
59
60 protected:
61   ProviderBase(const FileSpec &root) : m_root(root) {}
62
63 private:
64   /// Every provider knows where to dump its potential files.
65   FileSpec m_root;
66
67   virtual void anchor();
68   static char ID;
69 };
70
71 template <typename ThisProviderT> class Provider : public ProviderBase {
72 public:
73   static const void *ClassID() { return &ThisProviderT::ID; }
74
75   const void *DynamicClassID() const override { return &ThisProviderT::ID; }
76
77   llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
78   llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
79
80 protected:
81   using ProviderBase::ProviderBase; // Inherit constructor.
82 };
83
84 class FileProvider : public Provider<FileProvider> {
85 public:
86   struct Info {
87     static const char *name;
88     static const char *file;
89   };
90
91   FileProvider(const FileSpec &directory)
92       : Provider(directory),
93         m_collector(std::make_shared<llvm::FileCollector>(
94             directory.CopyByAppendingPathComponent("root").GetPath(),
95             directory.GetPath())) {}
96
97   std::shared_ptr<llvm::FileCollector> GetFileCollector() {
98     return m_collector;
99   }
100
101   void Keep() override {
102     auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
103     // Temporary files that are removed during execution can cause copy errors.
104     if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
105       return;
106     m_collector->writeMapping(mapping.GetPath());
107   }
108
109   static char ID;
110
111 private:
112   std::shared_ptr<llvm::FileCollector> m_collector;
113 };
114
115 /// Provider for the LLDB version number.
116 ///
117 /// When the reproducer is kept, it writes the lldb version to a file named
118 /// version.txt in the reproducer root.
119 class VersionProvider : public Provider<VersionProvider> {
120 public:
121   VersionProvider(const FileSpec &directory) : Provider(directory) {}
122   struct Info {
123     static const char *name;
124     static const char *file;
125   };
126   void SetVersion(std::string version) {
127     assert(m_version.empty());
128     m_version = std::move(version);
129   }
130   void Keep() override;
131   std::string m_version;
132   static char ID;
133 };
134
135 /// Provider for the LLDB current working directroy.
136 ///
137 /// When the reproducer is kept, it writes lldb's current working directory to
138 /// a file named cwd.txt in the reproducer root.
139 class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {
140 public:
141   WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) {
142     llvm::SmallString<128> cwd;
143     if (std::error_code EC = llvm::sys::fs::current_path(cwd))
144       return;
145     m_cwd = cwd.str();
146   }
147   struct Info {
148     static const char *name;
149     static const char *file;
150   };
151   void Keep() override;
152   std::string m_cwd;
153   static char ID;
154 };
155
156 class AbstractRecorder {
157 protected:
158   AbstractRecorder(const FileSpec &filename, std::error_code &ec)
159       : m_filename(filename.GetFilename().GetStringRef()),
160         m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}
161
162 public:
163   const FileSpec &GetFilename() { return m_filename; }
164
165   void Stop() {
166     assert(m_record);
167     m_record = false;
168   }
169
170 private:
171   FileSpec m_filename;
172
173 protected:
174   llvm::raw_fd_ostream m_os;
175   bool m_record;
176 };
177
178 class DataRecorder : public AbstractRecorder {
179 public:
180   DataRecorder(const FileSpec &filename, std::error_code &ec)
181       : AbstractRecorder(filename, ec) {}
182
183   static llvm::Expected<std::unique_ptr<DataRecorder>>
184   Create(const FileSpec &filename);
185
186   template <typename T> void Record(const T &t, bool newline = false) {
187     if (!m_record)
188       return;
189     m_os << t;
190     if (newline)
191       m_os << '\n';
192     m_os.flush();
193   }
194 };
195
196 class CommandProvider : public Provider<CommandProvider> {
197 public:
198   struct Info {
199     static const char *name;
200     static const char *file;
201   };
202
203   CommandProvider(const FileSpec &directory) : Provider(directory) {}
204
205   DataRecorder *GetNewDataRecorder();
206
207   void Keep() override;
208   void Discard() override;
209
210   static char ID;
211
212 private:
213   std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
214 };
215
216 /// The generator is responsible for the logic needed to generate a
217 /// reproducer. For doing so it relies on providers, who serialize data that
218 /// is necessary for reproducing  a failure.
219 class Generator final {
220
221 public:
222   Generator(FileSpec root);
223   ~Generator();
224
225   /// Method to indicate we want to keep the reproducer. If reproducer
226   /// generation is disabled, this does nothing.
227   void Keep();
228
229   /// Method to indicate we do not want to keep the reproducer. This is
230   /// unaffected by whether or not generation reproduction is enabled, as we
231   /// might need to clean up files already written to disk.
232   void Discard();
233
234   /// Create and register a new provider.
235   template <typename T> T *Create() {
236     std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root);
237     return static_cast<T *>(Register(std::move(provider)));
238   }
239
240   /// Get an existing provider.
241   template <typename T> T *Get() {
242     auto it = m_providers.find(T::ClassID());
243     if (it == m_providers.end())
244       return nullptr;
245     return static_cast<T *>(it->second.get());
246   }
247
248   /// Get a provider if it exists, otherwise create it.
249   template <typename T> T &GetOrCreate() {
250     auto *provider = Get<T>();
251     if (provider)
252       return *provider;
253     return *Create<T>();
254   }
255
256   const FileSpec &GetRoot() const;
257
258 private:
259   friend Reproducer;
260
261   ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
262
263   /// Builds and index with provider info.
264   void AddProvidersToIndex();
265
266   /// Map of provider IDs to provider instances.
267   llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
268   std::mutex m_providers_mutex;
269
270   /// The reproducer root directory.
271   FileSpec m_root;
272
273   /// Flag to ensure that we never call both keep and discard.
274   bool m_done = false;
275 };
276
277 class Loader final {
278 public:
279   Loader(FileSpec root);
280
281   template <typename T> FileSpec GetFile() {
282     if (!HasFile(T::file))
283       return {};
284
285     return GetRoot().CopyByAppendingPathComponent(T::file);
286   }
287
288   template <typename T> llvm::Expected<std::string> LoadBuffer() {
289     FileSpec file = GetFile<typename T::Info>();
290     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
291         llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
292     if (!buffer)
293       return llvm::errorCodeToError(buffer.getError());
294     return (*buffer)->getBuffer().str();
295   }
296
297   llvm::Error LoadIndex();
298
299   const FileSpec &GetRoot() const { return m_root; }
300
301 private:
302   bool HasFile(llvm::StringRef file);
303
304   FileSpec m_root;
305   std::vector<std::string> m_files;
306   bool m_loaded;
307 };
308
309 /// The reproducer enables clients to obtain access to the Generator and
310 /// Loader.
311 class Reproducer {
312 public:
313   static Reproducer &Instance();
314   static llvm::Error Initialize(ReproducerMode mode,
315                                 llvm::Optional<FileSpec> root);
316   static bool Initialized();
317   static void Terminate();
318
319   Reproducer() = default;
320
321   Generator *GetGenerator();
322   Loader *GetLoader();
323
324   const Generator *GetGenerator() const;
325   const Loader *GetLoader() const;
326
327   FileSpec GetReproducerPath() const;
328
329   bool IsCapturing() { return static_cast<bool>(m_generator); };
330   bool IsReplaying() { return static_cast<bool>(m_loader); };
331
332 protected:
333   llvm::Error SetCapture(llvm::Optional<FileSpec> root);
334   llvm::Error SetReplay(llvm::Optional<FileSpec> root);
335
336 private:
337   static llvm::Optional<Reproducer> &InstanceImpl();
338
339   llvm::Optional<Generator> m_generator;
340   llvm::Optional<Loader> m_loader;
341
342   mutable std::mutex m_mutex;
343 };
344
345 template <typename T> class MultiLoader {
346 public:
347   MultiLoader(std::vector<std::string> files) : m_files(files) {}
348
349   static std::unique_ptr<MultiLoader> Create(Loader *loader) {
350     if (!loader)
351       return {};
352
353     FileSpec file = loader->GetFile<typename T::Info>();
354     if (!file)
355       return {};
356
357     auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
358     if (auto err = error_or_file.getError())
359       return {};
360
361     std::vector<std::string> files;
362     llvm::yaml::Input yin((*error_or_file)->getBuffer());
363     yin >> files;
364
365     if (auto err = yin.error())
366       return {};
367
368     for (auto &file : files) {
369       FileSpec absolute_path =
370           loader->GetRoot().CopyByAppendingPathComponent(file);
371       file = absolute_path.GetPath();
372     }
373
374     return std::make_unique<MultiLoader<T>>(std::move(files));
375   }
376
377   llvm::Optional<std::string> GetNextFile() {
378     if (m_index >= m_files.size())
379       return {};
380     return m_files[m_index++];
381   }
382
383 private:
384   std::vector<std::string> m_files;
385   unsigned m_index = 0;
386 };
387
388 } // namespace repro
389 } // namespace lldb_private
390
391 #endif // LLDB_UTILITY_REPRODUCER_H