Reproducer.h revision 360784
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
22namespace lldb_private {
23namespace repro {
24
25class Reproducer;
26
27enum 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.
37class ProviderBase {
38public:
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
60protected:
61  ProviderBase(const FileSpec &root) : m_root(root) {}
62
63private:
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
71template <typename ThisProviderT> class Provider : public ProviderBase {
72public:
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
80protected:
81  using ProviderBase::ProviderBase; // Inherit constructor.
82};
83
84class FileProvider : public Provider<FileProvider> {
85public:
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
111private:
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.
119class VersionProvider : public Provider<VersionProvider> {
120public:
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.
139class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {
140public:
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
156class AbstractRecorder {
157protected:
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
162public:
163  const FileSpec &GetFilename() { return m_filename; }
164
165  void Stop() {
166    assert(m_record);
167    m_record = false;
168  }
169
170private:
171  FileSpec m_filename;
172
173protected:
174  llvm::raw_fd_ostream m_os;
175  bool m_record;
176};
177
178class DataRecorder : public AbstractRecorder {
179public:
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
196class CommandProvider : public Provider<CommandProvider> {
197public:
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
212private:
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.
219class Generator final {
220
221public:
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
258private:
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
277class Loader final {
278public:
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
301private:
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.
311class Reproducer {
312public:
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
332protected:
333  llvm::Error SetCapture(llvm::Optional<FileSpec> root);
334  llvm::Error SetReplay(llvm::Optional<FileSpec> root);
335
336private:
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
345template <typename T> class MultiLoader {
346public:
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
383private:
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
392