]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/lldb/include/lldb/Utility/Reproducer.h
Merge ^/vendor/lldb/dist up to its last change, and resolve conflicts.
[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 DataRecorder {
157 public:
158   DataRecorder(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   static llvm::Expected<std::unique_ptr<DataRecorder>>
163   Create(const FileSpec &filename);
164
165   template <typename T> void Record(const T &t, bool newline = false) {
166     if (!m_record)
167       return;
168     m_os << t;
169     if (newline)
170       m_os << '\n';
171     m_os.flush();
172   }
173
174   const FileSpec &GetFilename() { return m_filename; }
175
176   void Stop() {
177     assert(m_record);
178     m_record = false;
179   }
180
181 private:
182   FileSpec m_filename;
183   llvm::raw_fd_ostream m_os;
184   bool m_record;
185 };
186
187 class CommandProvider : public Provider<CommandProvider> {
188 public:
189   struct Info {
190     static const char *name;
191     static const char *file;
192   };
193
194   CommandProvider(const FileSpec &directory) : Provider(directory) {}
195
196   DataRecorder *GetNewDataRecorder();
197
198   void Keep() override;
199   void Discard() override;
200
201   static char ID;
202
203 private:
204   std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
205 };
206
207 class ProcessGDBRemoteProvider
208     : public repro::Provider<ProcessGDBRemoteProvider> {
209 public:
210   struct Info {
211     static const char *name;
212     static const char *file;
213   };
214
215   ProcessGDBRemoteProvider(const FileSpec &directory) : Provider(directory) {}
216
217   llvm::raw_ostream *GetHistoryStream();
218
219   void SetCallback(std::function<void()> callback) {
220     m_callback = std::move(callback);
221   }
222
223   void Keep() override { m_callback(); }
224   void Discard() override { m_callback(); }
225
226   static char ID;
227
228 private:
229   std::function<void()> m_callback;
230   std::unique_ptr<llvm::raw_fd_ostream> m_stream_up;
231 };
232
233 /// The generator is responsible for the logic needed to generate a
234 /// reproducer. For doing so it relies on providers, who serialize data that
235 /// is necessary for reproducing  a failure.
236 class Generator final {
237
238 public:
239   Generator(FileSpec root);
240   ~Generator();
241
242   /// Method to indicate we want to keep the reproducer. If reproducer
243   /// generation is disabled, this does nothing.
244   void Keep();
245
246   /// Method to indicate we do not want to keep the reproducer. This is
247   /// unaffected by whether or not generation reproduction is enabled, as we
248   /// might need to clean up files already written to disk.
249   void Discard();
250
251   /// Create and register a new provider.
252   template <typename T> T *Create() {
253     std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root);
254     return static_cast<T *>(Register(std::move(provider)));
255   }
256
257   /// Get an existing provider.
258   template <typename T> T *Get() {
259     auto it = m_providers.find(T::ClassID());
260     if (it == m_providers.end())
261       return nullptr;
262     return static_cast<T *>(it->second.get());
263   }
264
265   /// Get a provider if it exists, otherwise create it.
266   template <typename T> T &GetOrCreate() {
267     auto *provider = Get<T>();
268     if (provider)
269       return *provider;
270     return *Create<T>();
271   }
272
273   const FileSpec &GetRoot() const;
274
275 private:
276   friend Reproducer;
277
278   ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
279
280   /// Builds and index with provider info.
281   void AddProvidersToIndex();
282
283   /// Map of provider IDs to provider instances.
284   llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
285   std::mutex m_providers_mutex;
286
287   /// The reproducer root directory.
288   FileSpec m_root;
289
290   /// Flag to ensure that we never call both keep and discard.
291   bool m_done;
292 };
293
294 class Loader final {
295 public:
296   Loader(FileSpec root);
297
298   template <typename T> FileSpec GetFile() {
299     if (!HasFile(T::file))
300       return {};
301
302     return GetRoot().CopyByAppendingPathComponent(T::file);
303   }
304
305   template <typename T> llvm::Expected<std::string> LoadBuffer() {
306     FileSpec file = GetFile<typename T::Info>();
307     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
308         llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
309     if (!buffer)
310       return llvm::errorCodeToError(buffer.getError());
311     return (*buffer)->getBuffer().str();
312   }
313
314   llvm::Error LoadIndex();
315
316   const FileSpec &GetRoot() const { return m_root; }
317
318 private:
319   bool HasFile(llvm::StringRef file);
320
321   FileSpec m_root;
322   std::vector<std::string> m_files;
323   bool m_loaded;
324 };
325
326 /// The reproducer enables clients to obtain access to the Generator and
327 /// Loader.
328 class Reproducer {
329 public:
330   static Reproducer &Instance();
331   static llvm::Error Initialize(ReproducerMode mode,
332                                 llvm::Optional<FileSpec> root);
333   static bool Initialized();
334   static void Terminate();
335
336   Reproducer() = default;
337
338   Generator *GetGenerator();
339   Loader *GetLoader();
340
341   const Generator *GetGenerator() const;
342   const Loader *GetLoader() const;
343
344   FileSpec GetReproducerPath() const;
345
346   bool IsCapturing() { return static_cast<bool>(m_generator); };
347   bool IsReplaying() { return static_cast<bool>(m_loader); };
348
349 protected:
350   llvm::Error SetCapture(llvm::Optional<FileSpec> root);
351   llvm::Error SetReplay(llvm::Optional<FileSpec> root);
352
353 private:
354   static llvm::Optional<Reproducer> &InstanceImpl();
355
356   llvm::Optional<Generator> m_generator;
357   llvm::Optional<Loader> m_loader;
358
359   mutable std::mutex m_mutex;
360 };
361
362 /// Helper class for replaying commands through the reproducer.
363 class CommandLoader {
364 public:
365   CommandLoader(std::vector<std::string> files) : m_files(files) {}
366
367   static std::unique_ptr<CommandLoader> Create(Loader *loader);
368   llvm::Optional<std::string> GetNextFile();
369
370 private:
371   std::vector<std::string> m_files;
372   unsigned m_index = 0;
373 };
374
375 } // namespace repro
376 } // namespace lldb_private
377
378 #endif // LLDB_UTILITY_REPRODUCER_H