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