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/ADT/StringRef.h"
15 #include "llvm/Support/Error.h"
16 #include "llvm/Support/VirtualFileSystem.h"
17 #include "llvm/Support/YAMLTraits.h"
18 
19 #include <mutex>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 namespace lldb_private {
25 class UUID;
26 namespace repro {
27 
28 class Reproducer;
29 
30 enum class ReproducerMode {
31   Capture,
32   Replay,
33   PassiveReplay,
34   Off,
35 };
36 
37 /// The provider defines an interface for generating files needed for
38 /// reproducing.
39 ///
40 /// Different components will implement different providers.
41 class ProviderBase {
42 public:
43   virtual ~ProviderBase() = default;
44 
GetRoot()45   const FileSpec &GetRoot() const { return m_root; }
46 
47   /// The Keep method is called when it is decided that we need to keep the
48   /// data in order to provide a reproducer.
Keep()49   virtual void Keep(){};
50 
51   /// The Discard method is called when it is decided that we do not need to
52   /// keep any information and will not generate a reproducer.
Discard()53   virtual void Discard(){};
54 
55   // Returns the class ID for this type.
ClassID()56   static const void *ClassID() { return &ID; }
57 
58   // Returns the class ID for the dynamic type of this Provider instance.
59   virtual const void *DynamicClassID() const = 0;
60 
61   virtual llvm::StringRef GetName() const = 0;
62   virtual llvm::StringRef GetFile() const = 0;
63 
64 protected:
ProviderBase(const FileSpec & root)65   ProviderBase(const FileSpec &root) : m_root(root) {}
66 
67 private:
68   /// Every provider knows where to dump its potential files.
69   FileSpec m_root;
70 
71   virtual void anchor();
72   static char ID;
73 };
74 
75 template <typename ThisProviderT> class Provider : public ProviderBase {
76 public:
ClassID()77   static const void *ClassID() { return &ThisProviderT::ID; }
78 
DynamicClassID()79   const void *DynamicClassID() const override { return &ThisProviderT::ID; }
80 
GetName()81   llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
GetFile()82   llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
83 
84 protected:
85   using ProviderBase::ProviderBase; // Inherit constructor.
86 };
87 
88 /// The generator is responsible for the logic needed to generate a
89 /// reproducer. For doing so it relies on providers, who serialize data that
90 /// is necessary for reproducing  a failure.
91 class Generator final {
92 
93 public:
94   Generator(FileSpec root);
95   ~Generator();
96 
97   /// Method to indicate we want to keep the reproducer. If reproducer
98   /// generation is disabled, this does nothing.
99   void Keep();
100 
101   /// Method to indicate we do not want to keep the reproducer. This is
102   /// unaffected by whether or not generation reproduction is enabled, as we
103   /// might need to clean up files already written to disk.
104   void Discard();
105 
106   /// Enable or disable auto generate.
107   void SetAutoGenerate(bool b);
108 
109   /// Return whether auto generate is enabled.
110   bool IsAutoGenerate() const;
111 
112   /// Create and register a new provider.
Create()113   template <typename T> T *Create() {
114     std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root);
115     return static_cast<T *>(Register(std::move(provider)));
116   }
117 
118   /// Get an existing provider.
Get()119   template <typename T> T *Get() {
120     auto it = m_providers.find(T::ClassID());
121     if (it == m_providers.end())
122       return nullptr;
123     return static_cast<T *>(it->second.get());
124   }
125 
126   /// Get a provider if it exists, otherwise create it.
GetOrCreate()127   template <typename T> T &GetOrCreate() {
128     auto *provider = Get<T>();
129     if (provider)
130       return *provider;
131     return *Create<T>();
132   }
133 
134   const FileSpec &GetRoot() const;
135 
136 private:
137   friend Reproducer;
138 
139   ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
140 
141   /// Builds and index with provider info.
142   void AddProvidersToIndex();
143 
144   /// Map of provider IDs to provider instances.
145   llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
146   std::mutex m_providers_mutex;
147 
148   /// The reproducer root directory.
149   FileSpec m_root;
150 
151   /// Flag to ensure that we never call both keep and discard.
152   bool m_done = false;
153 
154   /// Flag to auto generate a reproducer when it would otherwise be discarded.
155   bool m_auto_generate = false;
156 };
157 
158 class Loader final {
159 public:
160   Loader(FileSpec root, bool passive = false);
161 
GetFile()162   template <typename T> FileSpec GetFile() {
163     if (!HasFile(T::file))
164       return {};
165 
166     return GetRoot().CopyByAppendingPathComponent(T::file);
167   }
168 
LoadBuffer()169   template <typename T> llvm::Expected<std::string> LoadBuffer() {
170     FileSpec file = GetFile<typename T::Info>();
171     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
172         llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
173     if (!buffer)
174       return llvm::errorCodeToError(buffer.getError());
175     return (*buffer)->getBuffer().str();
176   }
177 
178   llvm::Error LoadIndex();
179 
GetRoot()180   const FileSpec &GetRoot() const { return m_root; }
181 
IsPassiveReplay()182   bool IsPassiveReplay() const { return m_passive_replay; }
183 
184 private:
185   bool HasFile(llvm::StringRef file);
186 
187   FileSpec m_root;
188   std::vector<std::string> m_files;
189   bool m_loaded;
190   bool m_passive_replay;
191 };
192 
193 /// The reproducer enables clients to obtain access to the Generator and
194 /// Loader.
195 class Reproducer {
196 public:
197   static Reproducer &Instance();
198   static llvm::Error Initialize(ReproducerMode mode,
199                                 llvm::Optional<FileSpec> root);
200   static void Initialize();
201   static bool Initialized();
202   static void Terminate();
203 
204   Reproducer() = default;
205 
206   Generator *GetGenerator();
207   Loader *GetLoader();
208 
209   const Generator *GetGenerator() const;
210   const Loader *GetLoader() const;
211 
212   FileSpec GetReproducerPath() const;
213 
IsCapturing()214   bool IsCapturing() { return static_cast<bool>(m_generator); };
IsReplaying()215   bool IsReplaying() { return static_cast<bool>(m_loader); };
216 
217 protected:
218   llvm::Error SetCapture(llvm::Optional<FileSpec> root);
219   llvm::Error SetReplay(llvm::Optional<FileSpec> root, bool passive = false);
220 
221 private:
222   static llvm::Optional<Reproducer> &InstanceImpl();
223 
224   llvm::Optional<Generator> m_generator;
225   llvm::Optional<Loader> m_loader;
226 
227   mutable std::mutex m_mutex;
228 };
229 
230 class Verifier {
231 public:
Verifier(Loader * loader)232   Verifier(Loader *loader) : m_loader(loader) {}
233   void Verify(llvm::function_ref<void(llvm::StringRef)> error_callback,
234               llvm::function_ref<void(llvm::StringRef)> warning_callback,
235               llvm::function_ref<void(llvm::StringRef)> note_callback) const;
236 
237 private:
238   Loader *m_loader;
239 };
240 
241 struct ReplayOptions {
242   bool verify = true;
243   bool check_version = true;
244 };
245 
246 llvm::Error Finalize(Loader *loader);
247 llvm::Error Finalize(const FileSpec &root);
248 
249 } // namespace repro
250 } // namespace lldb_private
251 
252 #endif // LLDB_UTILITY_REPRODUCER_H
253