1343181Sdim//===-- Reproducer.h --------------------------------------------*- C++ -*-===//
2343181Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343181Sdim//
7343181Sdim//===----------------------------------------------------------------------===//
8343181Sdim
9343181Sdim#ifndef LLDB_UTILITY_REPRODUCER_H
10343181Sdim#define LLDB_UTILITY_REPRODUCER_H
11343181Sdim
12343181Sdim#include "lldb/Utility/FileSpec.h"
13343181Sdim#include "llvm/ADT/DenseMap.h"
14343181Sdim#include "llvm/Support/Error.h"
15360784Sdim#include "llvm/Support/FileCollector.h"
16343181Sdim#include "llvm/Support/YAMLTraits.h"
17343181Sdim
18343181Sdim#include <mutex>
19343181Sdim#include <string>
20343181Sdim#include <vector>
21343181Sdim
22343181Sdimnamespace lldb_private {
23343181Sdimnamespace repro {
24343181Sdim
25343181Sdimclass Reproducer;
26343181Sdim
27343181Sdimenum class ReproducerMode {
28343181Sdim  Capture,
29343181Sdim  Replay,
30343181Sdim  Off,
31343181Sdim};
32343181Sdim
33343181Sdim/// The provider defines an interface for generating files needed for
34353358Sdim/// reproducing.
35343181Sdim///
36343181Sdim/// Different components will implement different providers.
37343181Sdimclass ProviderBase {
38343181Sdimpublic:
39343181Sdim  virtual ~ProviderBase() = default;
40343181Sdim
41343181Sdim  const FileSpec &GetRoot() const { return m_root; }
42343181Sdim
43343181Sdim  /// The Keep method is called when it is decided that we need to keep the
44343181Sdim  /// data in order to provide a reproducer.
45343181Sdim  virtual void Keep(){};
46343181Sdim
47343181Sdim  /// The Discard method is called when it is decided that we do not need to
48343181Sdim  /// keep any information and will not generate a reproducer.
49343181Sdim  virtual void Discard(){};
50343181Sdim
51343181Sdim  // Returns the class ID for this type.
52343181Sdim  static const void *ClassID() { return &ID; }
53343181Sdim
54343181Sdim  // Returns the class ID for the dynamic type of this Provider instance.
55343181Sdim  virtual const void *DynamicClassID() const = 0;
56343181Sdim
57353358Sdim  virtual llvm::StringRef GetName() const = 0;
58353358Sdim  virtual llvm::StringRef GetFile() const = 0;
59353358Sdim
60343181Sdimprotected:
61343181Sdim  ProviderBase(const FileSpec &root) : m_root(root) {}
62343181Sdim
63343181Sdimprivate:
64343181Sdim  /// Every provider knows where to dump its potential files.
65343181Sdim  FileSpec m_root;
66343181Sdim
67343181Sdim  virtual void anchor();
68343181Sdim  static char ID;
69343181Sdim};
70343181Sdim
71343181Sdimtemplate <typename ThisProviderT> class Provider : public ProviderBase {
72343181Sdimpublic:
73343181Sdim  static const void *ClassID() { return &ThisProviderT::ID; }
74343181Sdim
75343181Sdim  const void *DynamicClassID() const override { return &ThisProviderT::ID; }
76343181Sdim
77353358Sdim  llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
78353358Sdim  llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
79353358Sdim
80343181Sdimprotected:
81343181Sdim  using ProviderBase::ProviderBase; // Inherit constructor.
82343181Sdim};
83343181Sdim
84353358Sdimclass FileProvider : public Provider<FileProvider> {
85353358Sdimpublic:
86353358Sdim  struct Info {
87353358Sdim    static const char *name;
88353358Sdim    static const char *file;
89353358Sdim  };
90353358Sdim
91353358Sdim  FileProvider(const FileSpec &directory)
92353358Sdim      : Provider(directory),
93360784Sdim        m_collector(std::make_shared<llvm::FileCollector>(
94360784Sdim            directory.CopyByAppendingPathComponent("root").GetPath(),
95360784Sdim            directory.GetPath())) {}
96360784Sdim
97360784Sdim  std::shared_ptr<llvm::FileCollector> GetFileCollector() {
98360784Sdim    return m_collector;
99353358Sdim  }
100353358Sdim
101353358Sdim  void Keep() override {
102353358Sdim    auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
103353358Sdim    // Temporary files that are removed during execution can cause copy errors.
104360784Sdim    if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
105353358Sdim      return;
106360784Sdim    m_collector->writeMapping(mapping.GetPath());
107353358Sdim  }
108353358Sdim
109353358Sdim  static char ID;
110353358Sdim
111353358Sdimprivate:
112360784Sdim  std::shared_ptr<llvm::FileCollector> m_collector;
113353358Sdim};
114353358Sdim
115353358Sdim/// Provider for the LLDB version number.
116353358Sdim///
117353358Sdim/// When the reproducer is kept, it writes the lldb version to a file named
118353358Sdim/// version.txt in the reproducer root.
119353358Sdimclass VersionProvider : public Provider<VersionProvider> {
120353358Sdimpublic:
121353358Sdim  VersionProvider(const FileSpec &directory) : Provider(directory) {}
122353358Sdim  struct Info {
123353358Sdim    static const char *name;
124353358Sdim    static const char *file;
125353358Sdim  };
126353358Sdim  void SetVersion(std::string version) {
127353358Sdim    assert(m_version.empty());
128353358Sdim    m_version = std::move(version);
129353358Sdim  }
130353358Sdim  void Keep() override;
131353358Sdim  std::string m_version;
132353358Sdim  static char ID;
133353358Sdim};
134353358Sdim
135360784Sdim/// Provider for the LLDB current working directroy.
136360784Sdim///
137360784Sdim/// When the reproducer is kept, it writes lldb's current working directory to
138360784Sdim/// a file named cwd.txt in the reproducer root.
139360784Sdimclass WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {
140353358Sdimpublic:
141360784Sdim  WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) {
142360784Sdim    llvm::SmallString<128> cwd;
143360784Sdim    if (std::error_code EC = llvm::sys::fs::current_path(cwd))
144353358Sdim      return;
145360784Sdim    m_cwd = cwd.str();
146353358Sdim  }
147360784Sdim  struct Info {
148360784Sdim    static const char *name;
149360784Sdim    static const char *file;
150360784Sdim  };
151360784Sdim  void Keep() override;
152360784Sdim  std::string m_cwd;
153360784Sdim  static char ID;
154360784Sdim};
155353358Sdim
156360784Sdimclass AbstractRecorder {
157360784Sdimprotected:
158360784Sdim  AbstractRecorder(const FileSpec &filename, std::error_code &ec)
159360784Sdim      : m_filename(filename.GetFilename().GetStringRef()),
160360784Sdim        m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}
161360784Sdim
162360784Sdimpublic:
163353358Sdim  const FileSpec &GetFilename() { return m_filename; }
164353358Sdim
165353358Sdim  void Stop() {
166353358Sdim    assert(m_record);
167353358Sdim    m_record = false;
168353358Sdim  }
169353358Sdim
170353358Sdimprivate:
171353358Sdim  FileSpec m_filename;
172360784Sdim
173360784Sdimprotected:
174353358Sdim  llvm::raw_fd_ostream m_os;
175353358Sdim  bool m_record;
176353358Sdim};
177353358Sdim
178360784Sdimclass DataRecorder : public AbstractRecorder {
179360784Sdimpublic:
180360784Sdim  DataRecorder(const FileSpec &filename, std::error_code &ec)
181360784Sdim      : AbstractRecorder(filename, ec) {}
182360784Sdim
183360784Sdim  static llvm::Expected<std::unique_ptr<DataRecorder>>
184360784Sdim  Create(const FileSpec &filename);
185360784Sdim
186360784Sdim  template <typename T> void Record(const T &t, bool newline = false) {
187360784Sdim    if (!m_record)
188360784Sdim      return;
189360784Sdim    m_os << t;
190360784Sdim    if (newline)
191360784Sdim      m_os << '\n';
192360784Sdim    m_os.flush();
193360784Sdim  }
194360784Sdim};
195360784Sdim
196353358Sdimclass CommandProvider : public Provider<CommandProvider> {
197353358Sdimpublic:
198353358Sdim  struct Info {
199353358Sdim    static const char *name;
200353358Sdim    static const char *file;
201353358Sdim  };
202353358Sdim
203353358Sdim  CommandProvider(const FileSpec &directory) : Provider(directory) {}
204353358Sdim
205353358Sdim  DataRecorder *GetNewDataRecorder();
206353358Sdim
207353358Sdim  void Keep() override;
208353358Sdim  void Discard() override;
209353358Sdim
210353358Sdim  static char ID;
211353358Sdim
212353358Sdimprivate:
213353358Sdim  std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
214353358Sdim};
215353358Sdim
216343181Sdim/// The generator is responsible for the logic needed to generate a
217343181Sdim/// reproducer. For doing so it relies on providers, who serialize data that
218343181Sdim/// is necessary for reproducing  a failure.
219343181Sdimclass Generator final {
220360784Sdim
221343181Sdimpublic:
222360784Sdim  Generator(FileSpec root);
223343181Sdim  ~Generator();
224343181Sdim
225343181Sdim  /// Method to indicate we want to keep the reproducer. If reproducer
226343181Sdim  /// generation is disabled, this does nothing.
227343181Sdim  void Keep();
228343181Sdim
229343181Sdim  /// Method to indicate we do not want to keep the reproducer. This is
230343181Sdim  /// unaffected by whether or not generation reproduction is enabled, as we
231343181Sdim  /// might need to clean up files already written to disk.
232343181Sdim  void Discard();
233343181Sdim
234343181Sdim  /// Create and register a new provider.
235343181Sdim  template <typename T> T *Create() {
236360784Sdim    std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root);
237343181Sdim    return static_cast<T *>(Register(std::move(provider)));
238343181Sdim  }
239343181Sdim
240343181Sdim  /// Get an existing provider.
241343181Sdim  template <typename T> T *Get() {
242343181Sdim    auto it = m_providers.find(T::ClassID());
243343181Sdim    if (it == m_providers.end())
244343181Sdim      return nullptr;
245343181Sdim    return static_cast<T *>(it->second.get());
246343181Sdim  }
247343181Sdim
248343181Sdim  /// Get a provider if it exists, otherwise create it.
249343181Sdim  template <typename T> T &GetOrCreate() {
250343181Sdim    auto *provider = Get<T>();
251343181Sdim    if (provider)
252343181Sdim      return *provider;
253343181Sdim    return *Create<T>();
254343181Sdim  }
255343181Sdim
256343181Sdim  const FileSpec &GetRoot() const;
257343181Sdim
258343181Sdimprivate:
259343181Sdim  friend Reproducer;
260343181Sdim
261343181Sdim  ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
262343181Sdim
263343181Sdim  /// Builds and index with provider info.
264343181Sdim  void AddProvidersToIndex();
265343181Sdim
266343181Sdim  /// Map of provider IDs to provider instances.
267343181Sdim  llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
268343181Sdim  std::mutex m_providers_mutex;
269343181Sdim
270343181Sdim  /// The reproducer root directory.
271343181Sdim  FileSpec m_root;
272343181Sdim
273343181Sdim  /// Flag to ensure that we never call both keep and discard.
274360784Sdim  bool m_done = false;
275343181Sdim};
276343181Sdim
277343181Sdimclass Loader final {
278343181Sdimpublic:
279360784Sdim  Loader(FileSpec root);
280343181Sdim
281353358Sdim  template <typename T> FileSpec GetFile() {
282353358Sdim    if (!HasFile(T::file))
283353358Sdim      return {};
284353358Sdim
285353358Sdim    return GetRoot().CopyByAppendingPathComponent(T::file);
286353358Sdim  }
287353358Sdim
288360784Sdim  template <typename T> llvm::Expected<std::string> LoadBuffer() {
289360784Sdim    FileSpec file = GetFile<typename T::Info>();
290360784Sdim    llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
291360784Sdim        llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
292360784Sdim    if (!buffer)
293360784Sdim      return llvm::errorCodeToError(buffer.getError());
294360784Sdim    return (*buffer)->getBuffer().str();
295360784Sdim  }
296360784Sdim
297343181Sdim  llvm::Error LoadIndex();
298343181Sdim
299343181Sdim  const FileSpec &GetRoot() const { return m_root; }
300343181Sdim
301343181Sdimprivate:
302353358Sdim  bool HasFile(llvm::StringRef file);
303353358Sdim
304343181Sdim  FileSpec m_root;
305353358Sdim  std::vector<std::string> m_files;
306343181Sdim  bool m_loaded;
307343181Sdim};
308343181Sdim
309343181Sdim/// The reproducer enables clients to obtain access to the Generator and
310343181Sdim/// Loader.
311343181Sdimclass Reproducer {
312343181Sdimpublic:
313343181Sdim  static Reproducer &Instance();
314343181Sdim  static llvm::Error Initialize(ReproducerMode mode,
315343181Sdim                                llvm::Optional<FileSpec> root);
316353358Sdim  static bool Initialized();
317343181Sdim  static void Terminate();
318343181Sdim
319343181Sdim  Reproducer() = default;
320343181Sdim
321343181Sdim  Generator *GetGenerator();
322343181Sdim  Loader *GetLoader();
323343181Sdim
324343181Sdim  const Generator *GetGenerator() const;
325343181Sdim  const Loader *GetLoader() const;
326343181Sdim
327343181Sdim  FileSpec GetReproducerPath() const;
328343181Sdim
329360784Sdim  bool IsCapturing() { return static_cast<bool>(m_generator); };
330360784Sdim  bool IsReplaying() { return static_cast<bool>(m_loader); };
331360784Sdim
332343181Sdimprotected:
333343181Sdim  llvm::Error SetCapture(llvm::Optional<FileSpec> root);
334343181Sdim  llvm::Error SetReplay(llvm::Optional<FileSpec> root);
335343181Sdim
336343181Sdimprivate:
337343181Sdim  static llvm::Optional<Reproducer> &InstanceImpl();
338343181Sdim
339343181Sdim  llvm::Optional<Generator> m_generator;
340343181Sdim  llvm::Optional<Loader> m_loader;
341343181Sdim
342343181Sdim  mutable std::mutex m_mutex;
343343181Sdim};
344343181Sdim
345360784Sdimtemplate <typename T> class MultiLoader {
346360784Sdimpublic:
347360784Sdim  MultiLoader(std::vector<std::string> files) : m_files(files) {}
348360784Sdim
349360784Sdim  static std::unique_ptr<MultiLoader> Create(Loader *loader) {
350360784Sdim    if (!loader)
351360784Sdim      return {};
352360784Sdim
353360784Sdim    FileSpec file = loader->GetFile<typename T::Info>();
354360784Sdim    if (!file)
355360784Sdim      return {};
356360784Sdim
357360784Sdim    auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
358360784Sdim    if (auto err = error_or_file.getError())
359360784Sdim      return {};
360360784Sdim
361360784Sdim    std::vector<std::string> files;
362360784Sdim    llvm::yaml::Input yin((*error_or_file)->getBuffer());
363360784Sdim    yin >> files;
364360784Sdim
365360784Sdim    if (auto err = yin.error())
366360784Sdim      return {};
367360784Sdim
368360784Sdim    for (auto &file : files) {
369360784Sdim      FileSpec absolute_path =
370360784Sdim          loader->GetRoot().CopyByAppendingPathComponent(file);
371360784Sdim      file = absolute_path.GetPath();
372360784Sdim    }
373360784Sdim
374360784Sdim    return std::make_unique<MultiLoader<T>>(std::move(files));
375360784Sdim  }
376360784Sdim
377360784Sdim  llvm::Optional<std::string> GetNextFile() {
378360784Sdim    if (m_index >= m_files.size())
379360784Sdim      return {};
380360784Sdim    return m_files[m_index++];
381360784Sdim  }
382360784Sdim
383360784Sdimprivate:
384360784Sdim  std::vector<std::string> m_files;
385360784Sdim  unsigned m_index = 0;
386360784Sdim};
387360784Sdim
388343181Sdim} // namespace repro
389343181Sdim} // namespace lldb_private
390343181Sdim
391343181Sdim#endif // LLDB_UTILITY_REPRODUCER_H
392