1 //===-- Reproducer.h --------------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef LLDB_UTILITY_REPRODUCER_H
10 #define LLDB_UTILITY_REPRODUCER_H
12 #include "lldb/Utility/FileCollector.h"
13 #include "lldb/Utility/FileSpec.h"
15 #include "llvm/ADT/DenseMap.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Support/YAMLTraits.h"
23 namespace lldb_private {
28 enum class ReproducerMode {
34 /// The provider defines an interface for generating files needed for
37 /// Different components will implement different providers.
40 virtual ~ProviderBase() = default;
42 const FileSpec &GetRoot() const { return m_root; }
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(){};
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(){};
52 // Returns the class ID for this type.
53 static const void *ClassID() { return &ID; }
55 // Returns the class ID for the dynamic type of this Provider instance.
56 virtual const void *DynamicClassID() const = 0;
58 virtual llvm::StringRef GetName() const = 0;
59 virtual llvm::StringRef GetFile() const = 0;
62 ProviderBase(const FileSpec &root) : m_root(root) {}
65 /// Every provider knows where to dump its potential files.
68 virtual void anchor();
72 template <typename ThisProviderT> class Provider : public ProviderBase {
74 static const void *ClassID() { return &ThisProviderT::ID; }
76 const void *DynamicClassID() const override { return &ThisProviderT::ID; }
78 llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
79 llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
82 using ProviderBase::ProviderBase; // Inherit constructor.
85 class FileProvider : public Provider<FileProvider> {
88 static const char *name;
89 static const char *file;
92 FileProvider(const FileSpec &directory)
93 : Provider(directory),
94 m_collector(directory.CopyByAppendingPathComponent("root"), directory) {
97 FileCollector &GetFileCollector() { return m_collector; }
99 void Keep() override {
100 auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
101 // Temporary files that are removed during execution can cause copy errors.
102 if (auto ec = m_collector.CopyFiles(/*stop_on_error=*/false))
104 m_collector.WriteMapping(mapping);
110 FileCollector m_collector;
113 /// Provider for the LLDB version number.
115 /// When the reproducer is kept, it writes the lldb version to a file named
116 /// version.txt in the reproducer root.
117 class VersionProvider : public Provider<VersionProvider> {
119 VersionProvider(const FileSpec &directory) : Provider(directory) {}
121 static const char *name;
122 static const char *file;
124 void SetVersion(std::string version) {
125 assert(m_version.empty());
126 m_version = std::move(version);
128 void Keep() override;
129 std::string m_version;
135 DataRecorder(const FileSpec &filename, std::error_code &ec)
136 : m_filename(filename.GetFilename().GetStringRef()),
137 m_os(filename.GetPath(), ec, llvm::sys::fs::F_Text), m_record(true) {}
139 static llvm::Expected<std::unique_ptr<DataRecorder>>
140 Create(const FileSpec &filename);
142 template <typename T> void Record(const T &t, bool newline = false) {
151 const FileSpec &GetFilename() { return m_filename; }
160 llvm::raw_fd_ostream m_os;
164 class CommandProvider : public Provider<CommandProvider> {
167 static const char *name;
168 static const char *file;
171 CommandProvider(const FileSpec &directory) : Provider(directory) {}
173 DataRecorder *GetNewDataRecorder();
175 void Keep() override;
176 void Discard() override;
181 std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
184 /// The generator is responsible for the logic needed to generate a
185 /// reproducer. For doing so it relies on providers, who serialize data that
186 /// is necessary for reproducing a failure.
187 class Generator final {
189 Generator(const FileSpec &root);
192 /// Method to indicate we want to keep the reproducer. If reproducer
193 /// generation is disabled, this does nothing.
196 /// Method to indicate we do not want to keep the reproducer. This is
197 /// unaffected by whether or not generation reproduction is enabled, as we
198 /// might need to clean up files already written to disk.
201 /// Create and register a new provider.
202 template <typename T> T *Create() {
203 std::unique_ptr<ProviderBase> provider = llvm::make_unique<T>(m_root);
204 return static_cast<T *>(Register(std::move(provider)));
207 /// Get an existing provider.
208 template <typename T> T *Get() {
209 auto it = m_providers.find(T::ClassID());
210 if (it == m_providers.end())
212 return static_cast<T *>(it->second.get());
215 /// Get a provider if it exists, otherwise create it.
216 template <typename T> T &GetOrCreate() {
217 auto *provider = Get<T>();
223 const FileSpec &GetRoot() const;
228 ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
230 /// Builds and index with provider info.
231 void AddProvidersToIndex();
233 /// Map of provider IDs to provider instances.
234 llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
235 std::mutex m_providers_mutex;
237 /// The reproducer root directory.
240 /// Flag to ensure that we never call both keep and discard.
246 Loader(const FileSpec &root);
248 template <typename T> FileSpec GetFile() {
249 if (!HasFile(T::file))
252 return GetRoot().CopyByAppendingPathComponent(T::file);
255 llvm::Error LoadIndex();
257 const FileSpec &GetRoot() const { return m_root; }
260 bool HasFile(llvm::StringRef file);
263 std::vector<std::string> m_files;
267 /// The reproducer enables clients to obtain access to the Generator and
271 static Reproducer &Instance();
272 static llvm::Error Initialize(ReproducerMode mode,
273 llvm::Optional<FileSpec> root);
274 static bool Initialized();
275 static void Terminate();
277 Reproducer() = default;
279 Generator *GetGenerator();
282 const Generator *GetGenerator() const;
283 const Loader *GetLoader() const;
285 FileSpec GetReproducerPath() const;
288 llvm::Error SetCapture(llvm::Optional<FileSpec> root);
289 llvm::Error SetReplay(llvm::Optional<FileSpec> root);
292 static llvm::Optional<Reproducer> &InstanceImpl();
294 llvm::Optional<Generator> m_generator;
295 llvm::Optional<Loader> m_loader;
297 mutable std::mutex m_mutex;
301 } // namespace lldb_private
303 #endif // LLDB_UTILITY_REPRODUCER_H