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