1343181Sdim//===-- Reproducer.h --------------------------------------------*- C++ -*-===// 2343181Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6343181Sdim// 7343181Sdim//===----------------------------------------------------------------------===// 8343181Sdim 9343181Sdim#ifndef LLDB_UTILITY_REPRODUCER_H 10343181Sdim#define LLDB_UTILITY_REPRODUCER_H 11343181Sdim 12343181Sdim#include "lldb/Utility/FileSpec.h" 13343181Sdim#include "llvm/ADT/DenseMap.h" 14343181Sdim#include "llvm/Support/Error.h" 15360784Sdim#include "llvm/Support/FileCollector.h" 16343181Sdim#include "llvm/Support/YAMLTraits.h" 17343181Sdim 18343181Sdim#include <mutex> 19343181Sdim#include <string> 20343181Sdim#include <vector> 21343181Sdim 22343181Sdimnamespace lldb_private { 23343181Sdimnamespace repro { 24343181Sdim 25343181Sdimclass Reproducer; 26343181Sdim 27343181Sdimenum class ReproducerMode { 28343181Sdim Capture, 29343181Sdim Replay, 30343181Sdim Off, 31343181Sdim}; 32343181Sdim 33343181Sdim/// The provider defines an interface for generating files needed for 34353358Sdim/// reproducing. 35343181Sdim/// 36343181Sdim/// Different components will implement different providers. 37343181Sdimclass ProviderBase { 38343181Sdimpublic: 39343181Sdim virtual ~ProviderBase() = default; 40343181Sdim 41343181Sdim const FileSpec &GetRoot() const { return m_root; } 42343181Sdim 43343181Sdim /// The Keep method is called when it is decided that we need to keep the 44343181Sdim /// data in order to provide a reproducer. 45343181Sdim virtual void Keep(){}; 46343181Sdim 47343181Sdim /// The Discard method is called when it is decided that we do not need to 48343181Sdim /// keep any information and will not generate a reproducer. 49343181Sdim virtual void Discard(){}; 50343181Sdim 51343181Sdim // Returns the class ID for this type. 52343181Sdim static const void *ClassID() { return &ID; } 53343181Sdim 54343181Sdim // Returns the class ID for the dynamic type of this Provider instance. 55343181Sdim virtual const void *DynamicClassID() const = 0; 56343181Sdim 57353358Sdim virtual llvm::StringRef GetName() const = 0; 58353358Sdim virtual llvm::StringRef GetFile() const = 0; 59353358Sdim 60343181Sdimprotected: 61343181Sdim ProviderBase(const FileSpec &root) : m_root(root) {} 62343181Sdim 63343181Sdimprivate: 64343181Sdim /// Every provider knows where to dump its potential files. 65343181Sdim FileSpec m_root; 66343181Sdim 67343181Sdim virtual void anchor(); 68343181Sdim static char ID; 69343181Sdim}; 70343181Sdim 71343181Sdimtemplate <typename ThisProviderT> class Provider : public ProviderBase { 72343181Sdimpublic: 73343181Sdim static const void *ClassID() { return &ThisProviderT::ID; } 74343181Sdim 75343181Sdim const void *DynamicClassID() const override { return &ThisProviderT::ID; } 76343181Sdim 77353358Sdim llvm::StringRef GetName() const override { return ThisProviderT::Info::name; } 78353358Sdim llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; } 79353358Sdim 80343181Sdimprotected: 81343181Sdim using ProviderBase::ProviderBase; // Inherit constructor. 82343181Sdim}; 83343181Sdim 84353358Sdimclass FileProvider : public Provider<FileProvider> { 85353358Sdimpublic: 86353358Sdim struct Info { 87353358Sdim static const char *name; 88353358Sdim static const char *file; 89353358Sdim }; 90353358Sdim 91353358Sdim FileProvider(const FileSpec &directory) 92353358Sdim : Provider(directory), 93360784Sdim m_collector(std::make_shared<llvm::FileCollector>( 94360784Sdim directory.CopyByAppendingPathComponent("root").GetPath(), 95360784Sdim directory.GetPath())) {} 96360784Sdim 97360784Sdim std::shared_ptr<llvm::FileCollector> GetFileCollector() { 98360784Sdim return m_collector; 99353358Sdim } 100353358Sdim 101353358Sdim void Keep() override { 102353358Sdim auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file); 103353358Sdim // Temporary files that are removed during execution can cause copy errors. 104360784Sdim if (auto ec = m_collector->copyFiles(/*stop_on_error=*/false)) 105353358Sdim return; 106360784Sdim m_collector->writeMapping(mapping.GetPath()); 107353358Sdim } 108353358Sdim 109353358Sdim static char ID; 110353358Sdim 111353358Sdimprivate: 112360784Sdim std::shared_ptr<llvm::FileCollector> m_collector; 113353358Sdim}; 114353358Sdim 115353358Sdim/// Provider for the LLDB version number. 116353358Sdim/// 117353358Sdim/// When the reproducer is kept, it writes the lldb version to a file named 118353358Sdim/// version.txt in the reproducer root. 119353358Sdimclass VersionProvider : public Provider<VersionProvider> { 120353358Sdimpublic: 121353358Sdim VersionProvider(const FileSpec &directory) : Provider(directory) {} 122353358Sdim struct Info { 123353358Sdim static const char *name; 124353358Sdim static const char *file; 125353358Sdim }; 126353358Sdim void SetVersion(std::string version) { 127353358Sdim assert(m_version.empty()); 128353358Sdim m_version = std::move(version); 129353358Sdim } 130353358Sdim void Keep() override; 131353358Sdim std::string m_version; 132353358Sdim static char ID; 133353358Sdim}; 134353358Sdim 135360784Sdim/// Provider for the LLDB current working directroy. 136360784Sdim/// 137360784Sdim/// When the reproducer is kept, it writes lldb's current working directory to 138360784Sdim/// a file named cwd.txt in the reproducer root. 139360784Sdimclass WorkingDirectoryProvider : public Provider<WorkingDirectoryProvider> { 140353358Sdimpublic: 141360784Sdim WorkingDirectoryProvider(const FileSpec &directory) : Provider(directory) { 142360784Sdim llvm::SmallString<128> cwd; 143360784Sdim if (std::error_code EC = llvm::sys::fs::current_path(cwd)) 144353358Sdim return; 145360784Sdim m_cwd = cwd.str(); 146353358Sdim } 147360784Sdim struct Info { 148360784Sdim static const char *name; 149360784Sdim static const char *file; 150360784Sdim }; 151360784Sdim void Keep() override; 152360784Sdim std::string m_cwd; 153360784Sdim static char ID; 154360784Sdim}; 155353358Sdim 156360784Sdimclass AbstractRecorder { 157360784Sdimprotected: 158360784Sdim AbstractRecorder(const FileSpec &filename, std::error_code &ec) 159360784Sdim : m_filename(filename.GetFilename().GetStringRef()), 160360784Sdim m_os(filename.GetPath(), ec, llvm::sys::fs::OF_Text), m_record(true) {} 161360784Sdim 162360784Sdimpublic: 163353358Sdim const FileSpec &GetFilename() { return m_filename; } 164353358Sdim 165353358Sdim void Stop() { 166353358Sdim assert(m_record); 167353358Sdim m_record = false; 168353358Sdim } 169353358Sdim 170353358Sdimprivate: 171353358Sdim FileSpec m_filename; 172360784Sdim 173360784Sdimprotected: 174353358Sdim llvm::raw_fd_ostream m_os; 175353358Sdim bool m_record; 176353358Sdim}; 177353358Sdim 178360784Sdimclass DataRecorder : public AbstractRecorder { 179360784Sdimpublic: 180360784Sdim DataRecorder(const FileSpec &filename, std::error_code &ec) 181360784Sdim : AbstractRecorder(filename, ec) {} 182360784Sdim 183360784Sdim static llvm::Expected<std::unique_ptr<DataRecorder>> 184360784Sdim Create(const FileSpec &filename); 185360784Sdim 186360784Sdim template <typename T> void Record(const T &t, bool newline = false) { 187360784Sdim if (!m_record) 188360784Sdim return; 189360784Sdim m_os << t; 190360784Sdim if (newline) 191360784Sdim m_os << '\n'; 192360784Sdim m_os.flush(); 193360784Sdim } 194360784Sdim}; 195360784Sdim 196353358Sdimclass CommandProvider : public Provider<CommandProvider> { 197353358Sdimpublic: 198353358Sdim struct Info { 199353358Sdim static const char *name; 200353358Sdim static const char *file; 201353358Sdim }; 202353358Sdim 203353358Sdim CommandProvider(const FileSpec &directory) : Provider(directory) {} 204353358Sdim 205353358Sdim DataRecorder *GetNewDataRecorder(); 206353358Sdim 207353358Sdim void Keep() override; 208353358Sdim void Discard() override; 209353358Sdim 210353358Sdim static char ID; 211353358Sdim 212353358Sdimprivate: 213353358Sdim std::vector<std::unique_ptr<DataRecorder>> m_data_recorders; 214353358Sdim}; 215353358Sdim 216343181Sdim/// The generator is responsible for the logic needed to generate a 217343181Sdim/// reproducer. For doing so it relies on providers, who serialize data that 218343181Sdim/// is necessary for reproducing a failure. 219343181Sdimclass Generator final { 220360784Sdim 221343181Sdimpublic: 222360784Sdim Generator(FileSpec root); 223343181Sdim ~Generator(); 224343181Sdim 225343181Sdim /// Method to indicate we want to keep the reproducer. If reproducer 226343181Sdim /// generation is disabled, this does nothing. 227343181Sdim void Keep(); 228343181Sdim 229343181Sdim /// Method to indicate we do not want to keep the reproducer. This is 230343181Sdim /// unaffected by whether or not generation reproduction is enabled, as we 231343181Sdim /// might need to clean up files already written to disk. 232343181Sdim void Discard(); 233343181Sdim 234343181Sdim /// Create and register a new provider. 235343181Sdim template <typename T> T *Create() { 236360784Sdim std::unique_ptr<ProviderBase> provider = std::make_unique<T>(m_root); 237343181Sdim return static_cast<T *>(Register(std::move(provider))); 238343181Sdim } 239343181Sdim 240343181Sdim /// Get an existing provider. 241343181Sdim template <typename T> T *Get() { 242343181Sdim auto it = m_providers.find(T::ClassID()); 243343181Sdim if (it == m_providers.end()) 244343181Sdim return nullptr; 245343181Sdim return static_cast<T *>(it->second.get()); 246343181Sdim } 247343181Sdim 248343181Sdim /// Get a provider if it exists, otherwise create it. 249343181Sdim template <typename T> T &GetOrCreate() { 250343181Sdim auto *provider = Get<T>(); 251343181Sdim if (provider) 252343181Sdim return *provider; 253343181Sdim return *Create<T>(); 254343181Sdim } 255343181Sdim 256343181Sdim const FileSpec &GetRoot() const; 257343181Sdim 258343181Sdimprivate: 259343181Sdim friend Reproducer; 260343181Sdim 261343181Sdim ProviderBase *Register(std::unique_ptr<ProviderBase> provider); 262343181Sdim 263343181Sdim /// Builds and index with provider info. 264343181Sdim void AddProvidersToIndex(); 265343181Sdim 266343181Sdim /// Map of provider IDs to provider instances. 267343181Sdim llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers; 268343181Sdim std::mutex m_providers_mutex; 269343181Sdim 270343181Sdim /// The reproducer root directory. 271343181Sdim FileSpec m_root; 272343181Sdim 273343181Sdim /// Flag to ensure that we never call both keep and discard. 274360784Sdim bool m_done = false; 275343181Sdim}; 276343181Sdim 277343181Sdimclass Loader final { 278343181Sdimpublic: 279360784Sdim Loader(FileSpec root); 280343181Sdim 281353358Sdim template <typename T> FileSpec GetFile() { 282353358Sdim if (!HasFile(T::file)) 283353358Sdim return {}; 284353358Sdim 285353358Sdim return GetRoot().CopyByAppendingPathComponent(T::file); 286353358Sdim } 287353358Sdim 288360784Sdim template <typename T> llvm::Expected<std::string> LoadBuffer() { 289360784Sdim FileSpec file = GetFile<typename T::Info>(); 290360784Sdim llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> buffer = 291360784Sdim llvm::vfs::getRealFileSystem()->getBufferForFile(file.GetPath()); 292360784Sdim if (!buffer) 293360784Sdim return llvm::errorCodeToError(buffer.getError()); 294360784Sdim return (*buffer)->getBuffer().str(); 295360784Sdim } 296360784Sdim 297343181Sdim llvm::Error LoadIndex(); 298343181Sdim 299343181Sdim const FileSpec &GetRoot() const { return m_root; } 300343181Sdim 301343181Sdimprivate: 302353358Sdim bool HasFile(llvm::StringRef file); 303353358Sdim 304343181Sdim FileSpec m_root; 305353358Sdim std::vector<std::string> m_files; 306343181Sdim bool m_loaded; 307343181Sdim}; 308343181Sdim 309343181Sdim/// The reproducer enables clients to obtain access to the Generator and 310343181Sdim/// Loader. 311343181Sdimclass Reproducer { 312343181Sdimpublic: 313343181Sdim static Reproducer &Instance(); 314343181Sdim static llvm::Error Initialize(ReproducerMode mode, 315343181Sdim llvm::Optional<FileSpec> root); 316353358Sdim static bool Initialized(); 317343181Sdim static void Terminate(); 318343181Sdim 319343181Sdim Reproducer() = default; 320343181Sdim 321343181Sdim Generator *GetGenerator(); 322343181Sdim Loader *GetLoader(); 323343181Sdim 324343181Sdim const Generator *GetGenerator() const; 325343181Sdim const Loader *GetLoader() const; 326343181Sdim 327343181Sdim FileSpec GetReproducerPath() const; 328343181Sdim 329360784Sdim bool IsCapturing() { return static_cast<bool>(m_generator); }; 330360784Sdim bool IsReplaying() { return static_cast<bool>(m_loader); }; 331360784Sdim 332343181Sdimprotected: 333343181Sdim llvm::Error SetCapture(llvm::Optional<FileSpec> root); 334343181Sdim llvm::Error SetReplay(llvm::Optional<FileSpec> root); 335343181Sdim 336343181Sdimprivate: 337343181Sdim static llvm::Optional<Reproducer> &InstanceImpl(); 338343181Sdim 339343181Sdim llvm::Optional<Generator> m_generator; 340343181Sdim llvm::Optional<Loader> m_loader; 341343181Sdim 342343181Sdim mutable std::mutex m_mutex; 343343181Sdim}; 344343181Sdim 345360784Sdimtemplate <typename T> class MultiLoader { 346360784Sdimpublic: 347360784Sdim MultiLoader(std::vector<std::string> files) : m_files(files) {} 348360784Sdim 349360784Sdim static std::unique_ptr<MultiLoader> Create(Loader *loader) { 350360784Sdim if (!loader) 351360784Sdim return {}; 352360784Sdim 353360784Sdim FileSpec file = loader->GetFile<typename T::Info>(); 354360784Sdim if (!file) 355360784Sdim return {}; 356360784Sdim 357360784Sdim auto error_or_file = llvm::MemoryBuffer::getFile(file.GetPath()); 358360784Sdim if (auto err = error_or_file.getError()) 359360784Sdim return {}; 360360784Sdim 361360784Sdim std::vector<std::string> files; 362360784Sdim llvm::yaml::Input yin((*error_or_file)->getBuffer()); 363360784Sdim yin >> files; 364360784Sdim 365360784Sdim if (auto err = yin.error()) 366360784Sdim return {}; 367360784Sdim 368360784Sdim for (auto &file : files) { 369360784Sdim FileSpec absolute_path = 370360784Sdim loader->GetRoot().CopyByAppendingPathComponent(file); 371360784Sdim file = absolute_path.GetPath(); 372360784Sdim } 373360784Sdim 374360784Sdim return std::make_unique<MultiLoader<T>>(std::move(files)); 375360784Sdim } 376360784Sdim 377360784Sdim llvm::Optional<std::string> GetNextFile() { 378360784Sdim if (m_index >= m_files.size()) 379360784Sdim return {}; 380360784Sdim return m_files[m_index++]; 381360784Sdim } 382360784Sdim 383360784Sdimprivate: 384360784Sdim std::vector<std::string> m_files; 385360784Sdim unsigned m_index = 0; 386360784Sdim}; 387360784Sdim 388343181Sdim} // namespace repro 389343181Sdim} // namespace lldb_private 390343181Sdim 391343181Sdim#endif // LLDB_UTILITY_REPRODUCER_H 392