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