1 //===-- Reproducer.h --------------------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef LLDB_UTILITY_REPRODUCER_H
11 #define LLDB_UTILITY_REPRODUCER_H
12 
13 #include "lldb/Utility/FileSpec.h"
14 
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/YAMLTraits.h"
18 
19 #include <mutex>
20 #include <string>
21 #include <vector>
22 
23 namespace lldb_private {
24 namespace repro {
25 
26 class Reproducer;
27 
28 enum class ReproducerMode {
29   Capture,
30   Replay,
31   Off,
32 };
33 
34 /// Abstraction for information associated with a provider. This information
35 /// is serialized into an index which is used by the loader.
36 struct ProviderInfo {
37   std::string name;
38   std::vector<std::string> files;
39 };
40 
41 /// The provider defines an interface for generating files needed for
42 /// reproducing. The provider must populate its ProviderInfo to communicate
43 /// its name and files to the index, before registering with the generator,
44 /// i.e. in the constructor.
45 ///
46 /// Different components will implement different providers.
47 class ProviderBase {
48 public:
49   virtual ~ProviderBase() = default;
50 
GetInfo()51   const ProviderInfo &GetInfo() const { return m_info; }
GetRoot()52   const FileSpec &GetRoot() const { return m_root; }
53 
54   /// The Keep method is called when it is decided that we need to keep the
55   /// data in order to provide a reproducer.
Keep()56   virtual void Keep(){};
57 
58   /// The Discard method is called when it is decided that we do not need to
59   /// keep any information and will not generate a reproducer.
Discard()60   virtual void Discard(){};
61 
62   // Returns the class ID for this type.
ClassID()63   static const void *ClassID() { return &ID; }
64 
65   // Returns the class ID for the dynamic type of this Provider instance.
66   virtual const void *DynamicClassID() const = 0;
67 
68 protected:
ProviderBase(const FileSpec & root)69   ProviderBase(const FileSpec &root) : m_root(root) {}
70 
71   /// Every provider keeps track of its own files.
72   ProviderInfo m_info;
73 private:
74   /// Every provider knows where to dump its potential files.
75   FileSpec m_root;
76 
77   virtual void anchor();
78   static char ID;
79 };
80 
81 template <typename ThisProviderT> class Provider : public ProviderBase {
82 public:
ClassID()83   static const void *ClassID() { return &ThisProviderT::ID; }
84 
DynamicClassID()85   const void *DynamicClassID() const override { return &ThisProviderT::ID; }
86 
87 protected:
88   using ProviderBase::ProviderBase; // Inherit constructor.
89 };
90 
91 /// The generator is responsible for the logic needed to generate a
92 /// reproducer. For doing so it relies on providers, who serialize data that
93 /// is necessary for reproducing  a failure.
94 class Generator final {
95 public:
96   Generator(const FileSpec &root);
97   ~Generator();
98 
99   /// Method to indicate we want to keep the reproducer. If reproducer
100   /// generation is disabled, this does nothing.
101   void Keep();
102 
103   /// Method to indicate we do not want to keep the reproducer. This is
104   /// unaffected by whether or not generation reproduction is enabled, as we
105   /// might need to clean up files already written to disk.
106   void Discard();
107 
108   /// Create and register a new provider.
Create()109   template <typename T> T *Create() {
110     std::unique_ptr<ProviderBase> provider = llvm::make_unique<T>(m_root);
111     return static_cast<T *>(Register(std::move(provider)));
112   }
113 
114   /// Get an existing provider.
Get()115   template <typename T> T *Get() {
116     auto it = m_providers.find(T::ClassID());
117     if (it == m_providers.end())
118       return nullptr;
119     return static_cast<T *>(it->second.get());
120   }
121 
122   /// Get a provider if it exists, otherwise create it.
GetOrCreate()123   template <typename T> T &GetOrCreate() {
124     auto *provider = Get<T>();
125     if (provider)
126       return *provider;
127     return *Create<T>();
128   }
129 
130   const FileSpec &GetRoot() const;
131 
132 private:
133   friend Reproducer;
134 
135   ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
136 
137   /// Builds and index with provider info.
138   void AddProvidersToIndex();
139 
140   /// Map of provider IDs to provider instances.
141   llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
142   std::mutex m_providers_mutex;
143 
144   /// The reproducer root directory.
145   FileSpec m_root;
146 
147   /// Flag to ensure that we never call both keep and discard.
148   bool m_done;
149 };
150 
151 class Loader final {
152 public:
153   Loader(const FileSpec &root);
154 
155   llvm::Optional<ProviderInfo> GetProviderInfo(llvm::StringRef name);
156   llvm::Error LoadIndex();
157 
GetRoot()158   const FileSpec &GetRoot() const { return m_root; }
159 
160 private:
161   llvm::StringMap<ProviderInfo> m_provider_info;
162   FileSpec m_root;
163   bool m_loaded;
164 };
165 
166 /// The reproducer enables clients to obtain access to the Generator and
167 /// Loader.
168 class Reproducer {
169 public:
170   static Reproducer &Instance();
171   static llvm::Error Initialize(ReproducerMode mode,
172                                 llvm::Optional<FileSpec> root);
173   static void Terminate();
174 
175   Reproducer() = default;
176 
177   Generator *GetGenerator();
178   Loader *GetLoader();
179 
180   const Generator *GetGenerator() const;
181   const Loader *GetLoader() const;
182 
183   FileSpec GetReproducerPath() const;
184 
185 protected:
186   llvm::Error SetCapture(llvm::Optional<FileSpec> root);
187   llvm::Error SetReplay(llvm::Optional<FileSpec> root);
188 
189 private:
190   static llvm::Optional<Reproducer> &InstanceImpl();
191 
192   llvm::Optional<Generator> m_generator;
193   llvm::Optional<Loader> m_loader;
194 
195   mutable std::mutex m_mutex;
196 };
197 
198 } // namespace repro
199 } // namespace lldb_private
200 
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::repro::ProviderInfo)201 LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::repro::ProviderInfo)
202 
203 namespace llvm {
204 namespace yaml {
205 
206 template <> struct MappingTraits<lldb_private::repro::ProviderInfo> {
207   static void mapping(IO &io, lldb_private::repro::ProviderInfo &info) {
208     io.mapRequired("name", info.name);
209     io.mapOptional("files", info.files);
210   }
211 };
212 } // namespace yaml
213 } // namespace llvm
214 
215 #endif // LLDB_UTILITY_REPRODUCER_H
216