Reproducer.h revision 353358
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
12353358Sdim#include "lldb/Utility/FileCollector.h"
13343181Sdim#include "lldb/Utility/FileSpec.h"
14343181Sdim
15343181Sdim#include "llvm/ADT/DenseMap.h"
16343181Sdim#include "llvm/Support/Error.h"
17343181Sdim#include "llvm/Support/YAMLTraits.h"
18343181Sdim
19343181Sdim#include <mutex>
20343181Sdim#include <string>
21343181Sdim#include <vector>
22343181Sdim
23343181Sdimnamespace lldb_private {
24343181Sdimnamespace repro {
25343181Sdim
26343181Sdimclass Reproducer;
27343181Sdim
28343181Sdimenum class ReproducerMode {
29343181Sdim  Capture,
30343181Sdim  Replay,
31343181Sdim  Off,
32343181Sdim};
33343181Sdim
34343181Sdim/// The provider defines an interface for generating files needed for
35353358Sdim/// reproducing.
36343181Sdim///
37343181Sdim/// Different components will implement different providers.
38343181Sdimclass ProviderBase {
39343181Sdimpublic:
40343181Sdim  virtual ~ProviderBase() = default;
41343181Sdim
42343181Sdim  const FileSpec &GetRoot() const { return m_root; }
43343181Sdim
44343181Sdim  /// The Keep method is called when it is decided that we need to keep the
45343181Sdim  /// data in order to provide a reproducer.
46343181Sdim  virtual void Keep(){};
47343181Sdim
48343181Sdim  /// The Discard method is called when it is decided that we do not need to
49343181Sdim  /// keep any information and will not generate a reproducer.
50343181Sdim  virtual void Discard(){};
51343181Sdim
52343181Sdim  // Returns the class ID for this type.
53343181Sdim  static const void *ClassID() { return &ID; }
54343181Sdim
55343181Sdim  // Returns the class ID for the dynamic type of this Provider instance.
56343181Sdim  virtual const void *DynamicClassID() const = 0;
57343181Sdim
58353358Sdim  virtual llvm::StringRef GetName() const = 0;
59353358Sdim  virtual llvm::StringRef GetFile() const = 0;
60353358Sdim
61343181Sdimprotected:
62343181Sdim  ProviderBase(const FileSpec &root) : m_root(root) {}
63343181Sdim
64343181Sdimprivate:
65343181Sdim  /// Every provider knows where to dump its potential files.
66343181Sdim  FileSpec m_root;
67343181Sdim
68343181Sdim  virtual void anchor();
69343181Sdim  static char ID;
70343181Sdim};
71343181Sdim
72343181Sdimtemplate <typename ThisProviderT> class Provider : public ProviderBase {
73343181Sdimpublic:
74343181Sdim  static const void *ClassID() { return &ThisProviderT::ID; }
75343181Sdim
76343181Sdim  const void *DynamicClassID() const override { return &ThisProviderT::ID; }
77343181Sdim
78353358Sdim  llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
79353358Sdim  llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
80353358Sdim
81343181Sdimprotected:
82343181Sdim  using ProviderBase::ProviderBase; // Inherit constructor.
83343181Sdim};
84343181Sdim
85353358Sdimclass FileProvider : public Provider<FileProvider> {
86353358Sdimpublic:
87353358Sdim  struct Info {
88353358Sdim    static const char *name;
89353358Sdim    static const char *file;
90353358Sdim  };
91353358Sdim
92353358Sdim  FileProvider(const FileSpec &directory)
93353358Sdim      : Provider(directory),
94353358Sdim        m_collector(directory.CopyByAppendingPathComponent("root"), directory) {
95353358Sdim  }
96353358Sdim
97353358Sdim  FileCollector &GetFileCollector() { return m_collector; }
98353358Sdim
99353358Sdim  void Keep() override {
100353358Sdim    auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
101353358Sdim    // Temporary files that are removed during execution can cause copy errors.
102353358Sdim    if (auto ec = m_collector.CopyFiles(/*stop_on_error=*/false))
103353358Sdim      return;
104353358Sdim    m_collector.WriteMapping(mapping);
105353358Sdim  }
106353358Sdim
107353358Sdim  static char ID;
108353358Sdim
109353358Sdimprivate:
110353358Sdim  FileCollector m_collector;
111353358Sdim};
112353358Sdim
113353358Sdim/// Provider for the LLDB version number.
114353358Sdim///
115353358Sdim/// When the reproducer is kept, it writes the lldb version to a file named
116353358Sdim/// version.txt in the reproducer root.
117353358Sdimclass VersionProvider : public Provider<VersionProvider> {
118353358Sdimpublic:
119353358Sdim  VersionProvider(const FileSpec &directory) : Provider(directory) {}
120353358Sdim  struct Info {
121353358Sdim    static const char *name;
122353358Sdim    static const char *file;
123353358Sdim  };
124353358Sdim  void SetVersion(std::string version) {
125353358Sdim    assert(m_version.empty());
126353358Sdim    m_version = std::move(version);
127353358Sdim  }
128353358Sdim  void Keep() override;
129353358Sdim  std::string m_version;
130353358Sdim  static char ID;
131353358Sdim};
132353358Sdim
133353358Sdimclass DataRecorder {
134353358Sdimpublic:
135353358Sdim  DataRecorder(const FileSpec &filename, std::error_code &ec)
136353358Sdim      : m_filename(filename.GetFilename().GetStringRef()),
137353358Sdim        m_os(filename.GetPath(), ec, llvm::sys::fs::F_Text), m_record(true) {}
138353358Sdim
139353358Sdim  static llvm::Expected<std::unique_ptr<DataRecorder>>
140353358Sdim  Create(const FileSpec &filename);
141353358Sdim
142353358Sdim  template <typename T> void Record(const T &t, bool newline = false) {
143353358Sdim    if (!m_record)
144353358Sdim      return;
145353358Sdim    m_os << t;
146353358Sdim    if (newline)
147353358Sdim      m_os << '\n';
148353358Sdim    m_os.flush();
149353358Sdim  }
150353358Sdim
151353358Sdim  const FileSpec &GetFilename() { return m_filename; }
152353358Sdim
153353358Sdim  void Stop() {
154353358Sdim    assert(m_record);
155353358Sdim    m_record = false;
156353358Sdim  }
157353358Sdim
158353358Sdimprivate:
159353358Sdim  FileSpec m_filename;
160353358Sdim  llvm::raw_fd_ostream m_os;
161353358Sdim  bool m_record;
162353358Sdim};
163353358Sdim
164353358Sdimclass CommandProvider : public Provider<CommandProvider> {
165353358Sdimpublic:
166353358Sdim  struct Info {
167353358Sdim    static const char *name;
168353358Sdim    static const char *file;
169353358Sdim  };
170353358Sdim
171353358Sdim  CommandProvider(const FileSpec &directory) : Provider(directory) {}
172353358Sdim
173353358Sdim  DataRecorder *GetNewDataRecorder();
174353358Sdim
175353358Sdim  void Keep() override;
176353358Sdim  void Discard() override;
177353358Sdim
178353358Sdim  static char ID;
179353358Sdim
180353358Sdimprivate:
181353358Sdim  std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
182353358Sdim};
183353358Sdim
184343181Sdim/// The generator is responsible for the logic needed to generate a
185343181Sdim/// reproducer. For doing so it relies on providers, who serialize data that
186343181Sdim/// is necessary for reproducing  a failure.
187343181Sdimclass Generator final {
188343181Sdimpublic:
189343181Sdim  Generator(const FileSpec &root);
190343181Sdim  ~Generator();
191343181Sdim
192343181Sdim  /// Method to indicate we want to keep the reproducer. If reproducer
193343181Sdim  /// generation is disabled, this does nothing.
194343181Sdim  void Keep();
195343181Sdim
196343181Sdim  /// Method to indicate we do not want to keep the reproducer. This is
197343181Sdim  /// unaffected by whether or not generation reproduction is enabled, as we
198343181Sdim  /// might need to clean up files already written to disk.
199343181Sdim  void Discard();
200343181Sdim
201343181Sdim  /// Create and register a new provider.
202343181Sdim  template <typename T> T *Create() {
203343181Sdim    std::unique_ptr<ProviderBase> provider = llvm::make_unique<T>(m_root);
204343181Sdim    return static_cast<T *>(Register(std::move(provider)));
205343181Sdim  }
206343181Sdim
207343181Sdim  /// Get an existing provider.
208343181Sdim  template <typename T> T *Get() {
209343181Sdim    auto it = m_providers.find(T::ClassID());
210343181Sdim    if (it == m_providers.end())
211343181Sdim      return nullptr;
212343181Sdim    return static_cast<T *>(it->second.get());
213343181Sdim  }
214343181Sdim
215343181Sdim  /// Get a provider if it exists, otherwise create it.
216343181Sdim  template <typename T> T &GetOrCreate() {
217343181Sdim    auto *provider = Get<T>();
218343181Sdim    if (provider)
219343181Sdim      return *provider;
220343181Sdim    return *Create<T>();
221343181Sdim  }
222343181Sdim
223343181Sdim  const FileSpec &GetRoot() const;
224343181Sdim
225343181Sdimprivate:
226343181Sdim  friend Reproducer;
227343181Sdim
228343181Sdim  ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
229343181Sdim
230343181Sdim  /// Builds and index with provider info.
231343181Sdim  void AddProvidersToIndex();
232343181Sdim
233343181Sdim  /// Map of provider IDs to provider instances.
234343181Sdim  llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
235343181Sdim  std::mutex m_providers_mutex;
236343181Sdim
237343181Sdim  /// The reproducer root directory.
238343181Sdim  FileSpec m_root;
239343181Sdim
240343181Sdim  /// Flag to ensure that we never call both keep and discard.
241343181Sdim  bool m_done;
242343181Sdim};
243343181Sdim
244343181Sdimclass Loader final {
245343181Sdimpublic:
246343181Sdim  Loader(const FileSpec &root);
247343181Sdim
248353358Sdim  template <typename T> FileSpec GetFile() {
249353358Sdim    if (!HasFile(T::file))
250353358Sdim      return {};
251353358Sdim
252353358Sdim    return GetRoot().CopyByAppendingPathComponent(T::file);
253353358Sdim  }
254353358Sdim
255343181Sdim  llvm::Error LoadIndex();
256343181Sdim
257343181Sdim  const FileSpec &GetRoot() const { return m_root; }
258343181Sdim
259343181Sdimprivate:
260353358Sdim  bool HasFile(llvm::StringRef file);
261353358Sdim
262343181Sdim  FileSpec m_root;
263353358Sdim  std::vector<std::string> m_files;
264343181Sdim  bool m_loaded;
265343181Sdim};
266343181Sdim
267343181Sdim/// The reproducer enables clients to obtain access to the Generator and
268343181Sdim/// Loader.
269343181Sdimclass Reproducer {
270343181Sdimpublic:
271343181Sdim  static Reproducer &Instance();
272343181Sdim  static llvm::Error Initialize(ReproducerMode mode,
273343181Sdim                                llvm::Optional<FileSpec> root);
274353358Sdim  static bool Initialized();
275343181Sdim  static void Terminate();
276343181Sdim
277343181Sdim  Reproducer() = default;
278343181Sdim
279343181Sdim  Generator *GetGenerator();
280343181Sdim  Loader *GetLoader();
281343181Sdim
282343181Sdim  const Generator *GetGenerator() const;
283343181Sdim  const Loader *GetLoader() const;
284343181Sdim
285343181Sdim  FileSpec GetReproducerPath() const;
286343181Sdim
287343181Sdimprotected:
288343181Sdim  llvm::Error SetCapture(llvm::Optional<FileSpec> root);
289343181Sdim  llvm::Error SetReplay(llvm::Optional<FileSpec> root);
290343181Sdim
291343181Sdimprivate:
292343181Sdim  static llvm::Optional<Reproducer> &InstanceImpl();
293343181Sdim
294343181Sdim  llvm::Optional<Generator> m_generator;
295343181Sdim  llvm::Optional<Loader> m_loader;
296343181Sdim
297343181Sdim  mutable std::mutex m_mutex;
298343181Sdim};
299343181Sdim
300343181Sdim} // namespace repro
301343181Sdim} // namespace lldb_private
302343181Sdim
303343181Sdim#endif // LLDB_UTILITY_REPRODUCER_H
304