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