Reproducer.h revision 360784
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/Support/Error.h" 15#include "llvm/Support/FileCollector.h" 16#include "llvm/Support/YAMLTraits.h" 17 18#include <mutex> 19#include <string> 20#include <vector> 21 22namespace lldb_private { 23namespace repro { 24 25class Reproducer; 26 27enum class ReproducerMode { 28 Capture, 29 Replay, 30 Off, 31}; 32 33/// The provider defines an interface for generating files needed for 34/// reproducing. 35/// 36/// Different components will implement different providers. 37class ProviderBase { 38public: 39 virtual ~ProviderBase() = default; 40 41 const FileSpec &GetRoot() const { return m_root; } 42 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(){}; 46 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(){}; 50 51 // Returns the class ID for this type. 52 static const void *ClassID() { return &ID; } 53 54 // Returns the class ID for the dynamic type of this Provider instance. 55 virtual const void *DynamicClassID() const = 0; 56 57 virtual llvm::StringRef GetName() const = 0; 58 virtual llvm::StringRef GetFile() const = 0; 59 60protected: 61 ProviderBase(const FileSpec &root) : m_root(root) {} 62 63private: 64 /// Every provider knows where to dump its potential files. 65 FileSpec m_root; 66 67 virtual void anchor(); 68 static char ID; 69}; 70 71template <typename ThisProviderT> class Provider : public ProviderBase { 72public: 73 static const void *ClassID() { return &ThisProviderT::ID; } 74 75 const void *DynamicClassID() const override { return &ThisProviderT::ID; } 76 77 llvm::StringRef GetName() const override { return ThisProviderT::Info::name; } 78 llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; } 79 80protected: 81 using ProviderBase::ProviderBase; // Inherit constructor. 82}; 83 84class FileProvider : public Provider<FileProvider> { 85public: 86 struct Info { 87 static const char *name; 88 static const char *file; 89 }; 90 91 FileProvider(const FileSpec &directory) 92 : Provider(directory), 93 m_collector(std::make_shared<llvm::FileCollector>( 94 directory.CopyByAppendingPathComponent("root").GetPath(), 95 directory.GetPath())) {} 96 97 std::shared_ptr<llvm::FileCollector> GetFileCollector() { 98 return m_collector; 99 } 100 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)) 105 return; 106 m_collector->writeMapping(mapping.GetPath()); 107 } 108 109 static char ID; 110 111private: 112 std::shared_ptr<llvm::FileCollector> m_collector; 113}; 114 115/// Provider for the LLDB version number. 116/// 117/// When the reproducer is kept, it writes the lldb version to a file named 118/// version.txt in the reproducer root. 119class VersionProvider : public Provider<VersionProvider> { 120public: 121 VersionProvider(const FileSpec &directory) : Provider(directory) {} 122 struct Info { 123 static const char *name; 124 static const char *file; 125 }; 126 void SetVersion(std::string version) { 127 assert(m_version.empty()); 128 m_version = std::move(version); 129 } 130 void Keep() override; 131 std::string m_version; 132 static char ID; 133}; 134 135/// Provider for the LLDB current working directroy. 136/// 137/// When the reproducer is kept, it writes lldb's current working directory to 138/// a file named cwd.txt in the reproducer root. 139class WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> { 140public: 141 WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) { 142 llvm::SmallString<128> cwd; 143 if (std::error_code EC = llvm::sys::fs::current_path(cwd)) 144 return; 145 m_cwd = cwd.str(); 146 } 147 struct Info { 148 static const char *name; 149 static const char *file; 150 }; 151 void Keep() override; 152 std::string m_cwd; 153 static char ID; 154}; 155 156class AbstractRecorder { 157protected: 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) {} 161 162public: 163 const FileSpec &GetFilename() { return m_filename; } 164 165 void Stop() { 166 assert(m_record); 167 m_record = false; 168 } 169 170private: 171 FileSpec m_filename; 172 173protected: 174 llvm::raw_fd_ostream m_os; 175 bool m_record; 176}; 177 178class DataRecorder : public AbstractRecorder { 179public: 180 DataRecorder(const FileSpec &filename, std::error_code &ec) 181 : AbstractRecorder(filename, ec) {} 182 183 static llvm::Expected<std::unique_ptr<DataRecorder>> 184 Create(const FileSpec &filename); 185 186 template <typename T> void Record(const T &t, bool newline = false) { 187 if (!m_record) 188 return; 189 m_os << t; 190 if (newline) 191 m_os << '\n'; 192 m_os.flush(); 193 } 194}; 195 196class CommandProvider : public Provider<CommandProvider> { 197public: 198 struct Info { 199 static const char *name; 200 static const char *file; 201 }; 202 203 CommandProvider(const FileSpec &directory) : Provider(directory) {} 204 205 DataRecorder *GetNewDataRecorder(); 206 207 void Keep() override; 208 void Discard() override; 209 210 static char ID; 211 212private: 213 std::vector<std::unique_ptr<DataRecorder>> m_data_recorders; 214}; 215 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. 219class Generator final { 220 221public: 222 Generator(FileSpec root); 223 ~Generator(); 224 225 /// Method to indicate we want to keep the reproducer. If reproducer 226 /// generation is disabled, this does nothing. 227 void Keep(); 228 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. 232 void Discard(); 233 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))); 238 } 239 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()) 244 return nullptr; 245 return static_cast<T *>(it->second.get()); 246 } 247 248 /// Get a provider if it exists, otherwise create it. 249 template <typename T> T &GetOrCreate() { 250 auto *provider = Get<T>(); 251 if (provider) 252 return *provider; 253 return *Create<T>(); 254 } 255 256 const FileSpec &GetRoot() const; 257 258private: 259 friend Reproducer; 260 261 ProviderBase *Register(std::unique_ptr<ProviderBase> provider); 262 263 /// Builds and index with provider info. 264 void AddProvidersToIndex(); 265 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; 269 270 /// The reproducer root directory. 271 FileSpec m_root; 272 273 /// Flag to ensure that we never call both keep and discard. 274 bool m_done = false; 275}; 276 277class Loader final { 278public: 279 Loader(FileSpec root); 280 281 template <typename T> FileSpec GetFile() { 282 if (!HasFile(T::file)) 283 return {}; 284 285 return GetRoot().CopyByAppendingPathComponent(T::file); 286 } 287 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()); 292 if (!buffer) 293 return llvm::errorCodeToError(buffer.getError()); 294 return (*buffer)->getBuffer().str(); 295 } 296 297 llvm::Error LoadIndex(); 298 299 const FileSpec &GetRoot() const { return m_root; } 300 301private: 302 bool HasFile(llvm::StringRef file); 303 304 FileSpec m_root; 305 std::vector<std::string> m_files; 306 bool m_loaded; 307}; 308 309/// The reproducer enables clients to obtain access to the Generator and 310/// Loader. 311class Reproducer { 312public: 313 static Reproducer &Instance(); 314 static llvm::Error Initialize(ReproducerMode mode, 315 llvm::Optional<FileSpec> root); 316 static bool Initialized(); 317 static void Terminate(); 318 319 Reproducer() = default; 320 321 Generator *GetGenerator(); 322 Loader *GetLoader(); 323 324 const Generator *GetGenerator() const; 325 const Loader *GetLoader() const; 326 327 FileSpec GetReproducerPath() const; 328 329 bool IsCapturing() { return static_cast<bool>(m_generator); }; 330 bool IsReplaying() { return static_cast<bool>(m_loader); }; 331 332protected: 333 llvm::Error SetCapture(llvm::Optional<FileSpec> root); 334 llvm::Error SetReplay(llvm::Optional<FileSpec> root); 335 336private: 337 static llvm::Optional<Reproducer> &InstanceImpl(); 338 339 llvm::Optional<Generator> m_generator; 340 llvm::Optional<Loader> m_loader; 341 342 mutable std::mutex m_mutex; 343}; 344 345template <typename T> class MultiLoader { 346public: 347 MultiLoader(std::vector<std::string> files) : m_files(files) {} 348 349 static std::unique_ptr<MultiLoader> Create(Loader *loader) { 350 if (!loader) 351 return {}; 352 353 FileSpec file = loader->GetFile<typename T::Info>(); 354 if (!file) 355 return {}; 356 357 auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); 358 if (auto err = error_or_file.getError()) 359 return {}; 360 361 std::vector<std::string> files; 362 llvm::yaml::Input yin((*error_or_file)->getBuffer()); 363 yin >> files; 364 365 if (auto err = yin.error()) 366 return {}; 367 368 for (auto &file : files) { 369 FileSpec absolute_path = 370 loader->GetRoot().CopyByAppendingPathComponent(file); 371 file = absolute_path.GetPath(); 372 } 373 374 return std::make_unique<MultiLoader<T>>(std::move(files)); 375 } 376 377 llvm::Optional<std::string> GetNextFile() { 378 if (m_index >= m_files.size()) 379 return {}; 380 return m_files[m_index++]; 381 } 382 383private: 384 std::vector<std::string> m_files; 385 unsigned m_index = 0; 386}; 387 388} // namespace repro 389} // namespace lldb_private 390 391#endif // LLDB_UTILITY_REPRODUCER_H 392