//===-- Reproducer.h --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef LLDB_UTILITY_REPRODUCER_H #define LLDB_UTILITY_REPRODUCER_H #include "lldb/Utility/FileSpec.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Error.h" #include "llvm/Support/YAMLTraits.h" #include #include #include namespace lldb_private { namespace repro { class Reproducer; enum class ReproducerMode { Capture, Replay, Off, }; /// Abstraction for information associated with a provider. This information /// is serialized into an index which is used by the loader. struct ProviderInfo { std::string name; std::vector files; }; /// The provider defines an interface for generating files needed for /// reproducing. The provider must populate its ProviderInfo to communicate /// its name and files to the index, before registering with the generator, /// i.e. in the constructor. /// /// Different components will implement different providers. class ProviderBase { public: virtual ~ProviderBase() = default; const ProviderInfo &GetInfo() const { return m_info; } const FileSpec &GetRoot() const { return m_root; } /// The Keep method is called when it is decided that we need to keep the /// data in order to provide a reproducer. virtual void Keep(){}; /// The Discard method is called when it is decided that we do not need to /// keep any information and will not generate a reproducer. virtual void Discard(){}; // Returns the class ID for this type. static const void *ClassID() { return &ID; } // Returns the class ID for the dynamic type of this Provider instance. virtual const void *DynamicClassID() const = 0; protected: ProviderBase(const FileSpec &root) : m_root(root) {} /// Every provider keeps track of its own files. ProviderInfo m_info; private: /// Every provider knows where to dump its potential files. FileSpec m_root; virtual void anchor(); static char ID; }; template class Provider : public ProviderBase { public: static const void *ClassID() { return &ThisProviderT::ID; } const void *DynamicClassID() const override { return &ThisProviderT::ID; } protected: using ProviderBase::ProviderBase; // Inherit constructor. }; /// The generator is responsible for the logic needed to generate a /// reproducer. For doing so it relies on providers, who serialize data that /// is necessary for reproducing a failure. class Generator final { public: Generator(const FileSpec &root); ~Generator(); /// Method to indicate we want to keep the reproducer. If reproducer /// generation is disabled, this does nothing. void Keep(); /// Method to indicate we do not want to keep the reproducer. This is /// unaffected by whether or not generation reproduction is enabled, as we /// might need to clean up files already written to disk. void Discard(); /// Create and register a new provider. template T *Create() { std::unique_ptr provider = llvm::make_unique(m_root); return static_cast(Register(std::move(provider))); } /// Get an existing provider. template T *Get() { auto it = m_providers.find(T::ClassID()); if (it == m_providers.end()) return nullptr; return static_cast(it->second.get()); } /// Get a provider if it exists, otherwise create it. template T &GetOrCreate() { auto *provider = Get(); if (provider) return *provider; return *Create(); } const FileSpec &GetRoot() const; private: friend Reproducer; ProviderBase *Register(std::unique_ptr provider); /// Builds and index with provider info. void AddProvidersToIndex(); /// Map of provider IDs to provider instances. llvm::DenseMap> m_providers; std::mutex m_providers_mutex; /// The reproducer root directory. FileSpec m_root; /// Flag to ensure that we never call both keep and discard. bool m_done; }; class Loader final { public: Loader(const FileSpec &root); llvm::Optional GetProviderInfo(llvm::StringRef name); llvm::Error LoadIndex(); const FileSpec &GetRoot() const { return m_root; } private: llvm::StringMap m_provider_info; FileSpec m_root; bool m_loaded; }; /// The reproducer enables clients to obtain access to the Generator and /// Loader. class Reproducer { public: static Reproducer &Instance(); static llvm::Error Initialize(ReproducerMode mode, llvm::Optional root); static void Terminate(); Reproducer() = default; Generator *GetGenerator(); Loader *GetLoader(); const Generator *GetGenerator() const; const Loader *GetLoader() const; FileSpec GetReproducerPath() const; protected: llvm::Error SetCapture(llvm::Optional root); llvm::Error SetReplay(llvm::Optional root); private: static llvm::Optional &InstanceImpl(); llvm::Optional m_generator; llvm::Optional m_loader; mutable std::mutex m_mutex; }; } // namespace repro } // namespace lldb_private LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(lldb_private::repro::ProviderInfo) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &io, lldb_private::repro::ProviderInfo &info) { io.mapRequired("name", info.name); io.mapOptional("files", info.files); } }; } // namespace yaml } // namespace llvm #endif // LLDB_UTILITY_REPRODUCER_H