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/FileSpec.h"
13 #include "llvm/ADT/DenseMap.h"
14 #include "llvm/Support/Error.h"
15 #include "llvm/Support/FileCollector.h"
16 #include "llvm/Support/YAMLTraits.h"
22 namespace lldb_private {
27 enum class ReproducerMode {
33 /// The provider defines an interface for generating files needed for
36 /// Different components will implement different providers.
39 virtual ~ProviderBase() = default;
41 const FileSpec &GetRoot() const { return m_root; }
43 /// The Keep method is called when it is decided that we need to keep the
44 /// data in order to provide a reproducer.
45 virtual void Keep(){};
47 /// The Discard method is called when it is decided that we do not need to
48 /// keep any information and will not generate a reproducer.
49 virtual void Discard(){};
51 // Returns the class ID for this type.
52 static const void *ClassID() { return &ID; }
54 // Returns the class ID for the dynamic type of this Provider instance.
55 virtual const void *DynamicClassID() const = 0;
57 virtual llvm::StringRef GetName() const = 0;
58 virtual llvm::StringRef GetFile() const = 0;
61 ProviderBase(const FileSpec &root) : m_root(root) {}
64 /// Every provider knows where to dump its potential files.
67 virtual void anchor();
71 template <typename ThisProviderT> class Provider : public ProviderBase {
73 static const void *ClassID() { return &ThisProviderT::ID; }
75 const void *DynamicClassID() const override { return &ThisProviderT::ID; }
77 llvm::StringRef GetName() const override { return ThisProviderT::Info::name; }
78 llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; }
81 using ProviderBase::ProviderBase; // Inherit constructor.
84 class FileProvider : public Provider<FileProvider> {
87 static const char *name;
88 static const char *file;
91 FileProvider(const FileSpec &directory)
92 : Provider(directory),
93 m_collector(std::make_shared<llvm::FileCollector>(
94 directory.CopyByAppendingPathComponent("root").GetPath(),
95 directory.GetPath())) {}
97 std::shared_ptr<llvm::FileCollector> GetFileCollector() {
101 void Keep() override {
102 auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file);
103 // Temporary files that are removed during execution can cause copy errors.
104 if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false))
106 m_collector->writeMapping(mapping.GetPath());
112 std::shared_ptr<llvm::FileCollector> m_collector;
115 /// Provider for the LLDB version number.
117 /// When the reproducer is kept, it writes the lldb version to a file named
118 /// version.txt in the reproducer root.
119 class VersionProvider : public Provider<VersionProvider> {
121 VersionProvider(const FileSpec &directory) : Provider(directory) {}
123 static const char *name;
124 static const char *file;
126 void SetVersion(std::string version) {
127 assert(m_version.empty());
128 m_version = std::move(version);
130 void Keep() override;
131 std::string m_version;
135 /// Provider for the LLDB current working directroy.
137 /// When the reproducer is kept, it writes lldb's current working directory to
138 /// a file named cwd.txt in the reproducer root.
139 class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> {
141 WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) {
142 llvm::SmallString<128> cwd;
143 if (std::error_code EC = llvm::sys::fs::current_path(cwd))
148 static const char *name;
149 static const char *file;
151 void Keep() override;
156 class AbstractRecorder {
158 AbstractRecorder(const FileSpec &filename, std::error_code &ec)
159 : m_filename(filename.GetFilename().GetStringRef()),
160 m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {}
163 const FileSpec &GetFilename() { return m_filename; }
174 llvm::raw_fd_ostream m_os;
178 class DataRecorder : public AbstractRecorder {
180 DataRecorder(const FileSpec &filename, std::error_code &ec)
181 : AbstractRecorder(filename, ec) {}
183 static llvm::Expected<std::unique_ptr<DataRecorder>>
184 Create(const FileSpec &filename);
186 template <typename T> void Record(const T &t, bool newline = false) {
196 class CommandProvider : public Provider<CommandProvider> {
199 static const char *name;
200 static const char *file;
203 CommandProvider(const FileSpec &directory) : Provider(directory) {}
205 DataRecorder *GetNewDataRecorder();
207 void Keep() override;
208 void Discard() override;
213 std::vector<std::unique_ptr<DataRecorder>> m_data_recorders;
216 /// The generator is responsible for the logic needed to generate a
217 /// reproducer. For doing so it relies on providers, who serialize data that
218 /// is necessary for reproducing a failure.
219 class Generator final {
222 Generator(FileSpec root);
225 /// Method to indicate we want to keep the reproducer. If reproducer
226 /// generation is disabled, this does nothing.
229 /// Method to indicate we do not want to keep the reproducer. This is
230 /// unaffected by whether or not generation reproduction is enabled, as we
231 /// might need to clean up files already written to disk.
234 /// Create and register a new provider.
235 template <typename T> T *Create() {
236 std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root);
237 return static_cast<T *>(Register(std::move(provider)));
240 /// Get an existing provider.
241 template <typename T> T *Get() {
242 auto it = m_providers.find(T::ClassID());
243 if (it == m_providers.end())
245 return static_cast<T *>(it->second.get());
248 /// Get a provider if it exists, otherwise create it.
249 template <typename T> T &GetOrCreate() {
250 auto *provider = Get<T>();
256 const FileSpec &GetRoot() const;
261 ProviderBase *Register(std::unique_ptr<ProviderBase> provider);
263 /// Builds and index with provider info.
264 void AddProvidersToIndex();
266 /// Map of provider IDs to provider instances.
267 llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers;
268 std::mutex m_providers_mutex;
270 /// The reproducer root directory.
273 /// Flag to ensure that we never call both keep and discard.
279 Loader(FileSpec root);
281 template <typename T> FileSpec GetFile() {
282 if (!HasFile(T::file))
285 return GetRoot().CopyByAppendingPathComponent(T::file);
288 template <typename T> llvm::Expected<std::string> LoadBuffer() {
289 FileSpec file = GetFile<typename T::Info>();
290 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer =
291 llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath());
293 return llvm::errorCodeToError(buffer.getError());
294 return (*buffer)->getBuffer().str();
297 llvm::Error LoadIndex();
299 const FileSpec &GetRoot() const { return m_root; }
302 bool HasFile(llvm::StringRef file);
305 std::vector<std::string> m_files;
309 /// The reproducer enables clients to obtain access to the Generator and
313 static Reproducer &Instance();
314 static llvm::Error Initialize(ReproducerMode mode,
315 llvm::Optional<FileSpec> root);
316 static bool Initialized();
317 static void Terminate();
319 Reproducer() = default;
321 Generator *GetGenerator();
324 const Generator *GetGenerator() const;
325 const Loader *GetLoader() const;
327 FileSpec GetReproducerPath() const;
329 bool IsCapturing() { return static_cast<bool>(m_generator); };
330 bool IsReplaying() { return static_cast<bool>(m_loader); };
333 llvm::Error SetCapture(llvm::Optional<FileSpec> root);
334 llvm::Error SetReplay(llvm::Optional<FileSpec> root);
337 static llvm::Optional<Reproducer> &InstanceImpl();
339 llvm::Optional<Generator> m_generator;
340 llvm::Optional<Loader> m_loader;
342 mutable std::mutex m_mutex;
345 template <typename T> class MultiLoader {
347 MultiLoader(std::vector<std::string> files) : m_files(files) {}
349 static std::unique_ptr<MultiLoader> Create(Loader *loader) {
353 FileSpec file = loader->GetFile<typename T::Info>();
357 auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath());
358 if (auto err = error_or_file.getError())
361 std::vector<std::string> files;
362 llvm::yaml::Input yin((*error_or_file)->getBuffer());
365 if (auto err = yin.error())
368 for (auto &file : files) {
369 FileSpec absolute_path =
370 loader->GetRoot().CopyByAppendingPathComponent(file);
371 file = absolute_path.GetPath();
374 return std::make_unique<MultiLoader<T>>(std::move(files));
377 llvm::Optional<std::string> GetNextFile() {
378 if (m_index >= m_files.size())
380 return m_files[m_index++];
384 std::vector<std::string> m_files;
385 unsigned m_index = 0;
389 } // namespace lldb_private
391 #endif // LLDB_UTILITY_REPRODUCER_H