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