Reproducer.h revision 353358
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 12353358Sdim#include "lldb/Utility/FileCollector.h" 13343181Sdim#include "lldb/Utility/FileSpec.h" 14343181Sdim 15343181Sdim#include "llvm/ADT/DenseMap.h" 16343181Sdim#include "llvm/Support/Error.h" 17343181Sdim#include "llvm/Support/YAMLTraits.h" 18343181Sdim 19343181Sdim#include <mutex> 20343181Sdim#include <string> 21343181Sdim#include <vector> 22343181Sdim 23343181Sdimnamespace lldb_private { 24343181Sdimnamespace repro { 25343181Sdim 26343181Sdimclass Reproducer; 27343181Sdim 28343181Sdimenum class ReproducerMode { 29343181Sdim Capture, 30343181Sdim Replay, 31343181Sdim Off, 32343181Sdim}; 33343181Sdim 34343181Sdim/// The provider defines an interface for generating files needed for 35353358Sdim/// reproducing. 36343181Sdim/// 37343181Sdim/// Different components will implement different providers. 38343181Sdimclass ProviderBase { 39343181Sdimpublic: 40343181Sdim virtual ~ProviderBase() = default; 41343181Sdim 42343181Sdim const FileSpec &GetRoot() const { return m_root; } 43343181Sdim 44343181Sdim /// The Keep method is called when it is decided that we need to keep the 45343181Sdim /// data in order to provide a reproducer. 46343181Sdim virtual void Keep(){}; 47343181Sdim 48343181Sdim /// The Discard method is called when it is decided that we do not need to 49343181Sdim /// keep any information and will not generate a reproducer. 50343181Sdim virtual void Discard(){}; 51343181Sdim 52343181Sdim // Returns the class ID for this type. 53343181Sdim static const void *ClassID() { return &ID; } 54343181Sdim 55343181Sdim // Returns the class ID for the dynamic type of this Provider instance. 56343181Sdim virtual const void *DynamicClassID() const = 0; 57343181Sdim 58353358Sdim virtual llvm::StringRef GetName() const = 0; 59353358Sdim virtual llvm::StringRef GetFile() const = 0; 60353358Sdim 61343181Sdimprotected: 62343181Sdim ProviderBase(const FileSpec &root) : m_root(root) {} 63343181Sdim 64343181Sdimprivate: 65343181Sdim /// Every provider knows where to dump its potential files. 66343181Sdim FileSpec m_root; 67343181Sdim 68343181Sdim virtual void anchor(); 69343181Sdim static char ID; 70343181Sdim}; 71343181Sdim 72343181Sdimtemplate <typename ThisProviderT> class Provider : public ProviderBase { 73343181Sdimpublic: 74343181Sdim static const void *ClassID() { return &ThisProviderT::ID; } 75343181Sdim 76343181Sdim const void *DynamicClassID() const override { return &ThisProviderT::ID; } 77343181Sdim 78353358Sdim llvm::StringRef GetName() const override { return ThisProviderT::Info::name; } 79353358Sdim llvm::StringRef GetFile() const override { return ThisProviderT::Info::file; } 80353358Sdim 81343181Sdimprotected: 82343181Sdim using ProviderBase::ProviderBase; // Inherit constructor. 83343181Sdim}; 84343181Sdim 85353358Sdimclass FileProvider : public Provider<FileProvider> { 86353358Sdimpublic: 87353358Sdim struct Info { 88353358Sdim static const char *name; 89353358Sdim static const char *file; 90353358Sdim }; 91353358Sdim 92353358Sdim FileProvider(const FileSpec &directory) 93353358Sdim : Provider(directory), 94353358Sdim m_collector(directory.CopyByAppendingPathComponent("root"), directory) { 95353358Sdim } 96353358Sdim 97353358Sdim FileCollector &GetFileCollector() { return m_collector; } 98353358Sdim 99353358Sdim void Keep() override { 100353358Sdim auto mapping = GetRoot().CopyByAppendingPathComponent(Info::file); 101353358Sdim // Temporary files that are removed during execution can cause copy errors. 102353358Sdim if (auto ec = m_collector.CopyFiles(/*stop_on_error=*/false)) 103353358Sdim return; 104353358Sdim m_collector.WriteMapping(mapping); 105353358Sdim } 106353358Sdim 107353358Sdim static char ID; 108353358Sdim 109353358Sdimprivate: 110353358Sdim FileCollector m_collector; 111353358Sdim}; 112353358Sdim 113353358Sdim/// Provider for the LLDB version number. 114353358Sdim/// 115353358Sdim/// When the reproducer is kept, it writes the lldb version to a file named 116353358Sdim/// version.txt in the reproducer root. 117353358Sdimclass VersionProvider : public Provider<VersionProvider> { 118353358Sdimpublic: 119353358Sdim VersionProvider(const FileSpec &directory) : Provider(directory) {} 120353358Sdim struct Info { 121353358Sdim static const char *name; 122353358Sdim static const char *file; 123353358Sdim }; 124353358Sdim void SetVersion(std::string version) { 125353358Sdim assert(m_version.empty()); 126353358Sdim m_version = std::move(version); 127353358Sdim } 128353358Sdim void Keep() override; 129353358Sdim std::string m_version; 130353358Sdim static char ID; 131353358Sdim}; 132353358Sdim 133353358Sdimclass DataRecorder { 134353358Sdimpublic: 135353358Sdim DataRecorder(const FileSpec &filename, std::error_code &ec) 136353358Sdim : m_filename(filename.GetFilename().GetStringRef()), 137353358Sdim m_os(filename.GetPath(), ec, llvm::sys::fs::F_Text), m_record(true) {} 138353358Sdim 139353358Sdim static llvm::Expected<std::unique_ptr<DataRecorder>> 140353358Sdim Create(const FileSpec &filename); 141353358Sdim 142353358Sdim template <typename T> void Record(const T &t, bool newline = false) { 143353358Sdim if (!m_record) 144353358Sdim return; 145353358Sdim m_os << t; 146353358Sdim if (newline) 147353358Sdim m_os << '\n'; 148353358Sdim m_os.flush(); 149353358Sdim } 150353358Sdim 151353358Sdim const FileSpec &GetFilename() { return m_filename; } 152353358Sdim 153353358Sdim void Stop() { 154353358Sdim assert(m_record); 155353358Sdim m_record = false; 156353358Sdim } 157353358Sdim 158353358Sdimprivate: 159353358Sdim FileSpec m_filename; 160353358Sdim llvm::raw_fd_ostream m_os; 161353358Sdim bool m_record; 162353358Sdim}; 163353358Sdim 164353358Sdimclass CommandProvider : public Provider<CommandProvider> { 165353358Sdimpublic: 166353358Sdim struct Info { 167353358Sdim static const char *name; 168353358Sdim static const char *file; 169353358Sdim }; 170353358Sdim 171353358Sdim CommandProvider(const FileSpec &directory) : Provider(directory) {} 172353358Sdim 173353358Sdim DataRecorder *GetNewDataRecorder(); 174353358Sdim 175353358Sdim void Keep() override; 176353358Sdim void Discard() override; 177353358Sdim 178353358Sdim static char ID; 179353358Sdim 180353358Sdimprivate: 181353358Sdim std::vector<std::unique_ptr<DataRecorder>> m_data_recorders; 182353358Sdim}; 183353358Sdim 184343181Sdim/// The generator is responsible for the logic needed to generate a 185343181Sdim/// reproducer. For doing so it relies on providers, who serialize data that 186343181Sdim/// is necessary for reproducing a failure. 187343181Sdimclass Generator final { 188343181Sdimpublic: 189343181Sdim Generator(const FileSpec &root); 190343181Sdim ~Generator(); 191343181Sdim 192343181Sdim /// Method to indicate we want to keep the reproducer. If reproducer 193343181Sdim /// generation is disabled, this does nothing. 194343181Sdim void Keep(); 195343181Sdim 196343181Sdim /// Method to indicate we do not want to keep the reproducer. This is 197343181Sdim /// unaffected by whether or not generation reproduction is enabled, as we 198343181Sdim /// might need to clean up files already written to disk. 199343181Sdim void Discard(); 200343181Sdim 201343181Sdim /// Create and register a new provider. 202343181Sdim template <typename T> T *Create() { 203343181Sdim std::unique_ptr<ProviderBase> provider = llvm::make_unique<T>(m_root); 204343181Sdim return static_cast<T *>(Register(std::move(provider))); 205343181Sdim } 206343181Sdim 207343181Sdim /// Get an existing provider. 208343181Sdim template <typename T> T *Get() { 209343181Sdim auto it = m_providers.find(T::ClassID()); 210343181Sdim if (it == m_providers.end()) 211343181Sdim return nullptr; 212343181Sdim return static_cast<T *>(it->second.get()); 213343181Sdim } 214343181Sdim 215343181Sdim /// Get a provider if it exists, otherwise create it. 216343181Sdim template <typename T> T &GetOrCreate() { 217343181Sdim auto *provider = Get<T>(); 218343181Sdim if (provider) 219343181Sdim return *provider; 220343181Sdim return *Create<T>(); 221343181Sdim } 222343181Sdim 223343181Sdim const FileSpec &GetRoot() const; 224343181Sdim 225343181Sdimprivate: 226343181Sdim friend Reproducer; 227343181Sdim 228343181Sdim ProviderBase *Register(std::unique_ptr<ProviderBase> provider); 229343181Sdim 230343181Sdim /// Builds and index with provider info. 231343181Sdim void AddProvidersToIndex(); 232343181Sdim 233343181Sdim /// Map of provider IDs to provider instances. 234343181Sdim llvm::DenseMap<const void *, std::unique_ptr<ProviderBase>> m_providers; 235343181Sdim std::mutex m_providers_mutex; 236343181Sdim 237343181Sdim /// The reproducer root directory. 238343181Sdim FileSpec m_root; 239343181Sdim 240343181Sdim /// Flag to ensure that we never call both keep and discard. 241343181Sdim bool m_done; 242343181Sdim}; 243343181Sdim 244343181Sdimclass Loader final { 245343181Sdimpublic: 246343181Sdim Loader(const FileSpec &root); 247343181Sdim 248353358Sdim template <typename T> FileSpec GetFile() { 249353358Sdim if (!HasFile(T::file)) 250353358Sdim return {}; 251353358Sdim 252353358Sdim return GetRoot().CopyByAppendingPathComponent(T::file); 253353358Sdim } 254353358Sdim 255343181Sdim llvm::Error LoadIndex(); 256343181Sdim 257343181Sdim const FileSpec &GetRoot() const { return m_root; } 258343181Sdim 259343181Sdimprivate: 260353358Sdim bool HasFile(llvm::StringRef file); 261353358Sdim 262343181Sdim FileSpec m_root; 263353358Sdim std::vector<std::string> m_files; 264343181Sdim bool m_loaded; 265343181Sdim}; 266343181Sdim 267343181Sdim/// The reproducer enables clients to obtain access to the Generator and 268343181Sdim/// Loader. 269343181Sdimclass Reproducer { 270343181Sdimpublic: 271343181Sdim static Reproducer &Instance(); 272343181Sdim static llvm::Error Initialize(ReproducerMode mode, 273343181Sdim llvm::Optional<FileSpec> root); 274353358Sdim static bool Initialized(); 275343181Sdim static void Terminate(); 276343181Sdim 277343181Sdim Reproducer() = default; 278343181Sdim 279343181Sdim Generator *GetGenerator(); 280343181Sdim Loader *GetLoader(); 281343181Sdim 282343181Sdim const Generator *GetGenerator() const; 283343181Sdim const Loader *GetLoader() const; 284343181Sdim 285343181Sdim FileSpec GetReproducerPath() const; 286343181Sdim 287343181Sdimprotected: 288343181Sdim llvm::Error SetCapture(llvm::Optional<FileSpec> root); 289343181Sdim llvm::Error SetReplay(llvm::Optional<FileSpec> root); 290343181Sdim 291343181Sdimprivate: 292343181Sdim static llvm::Optional<Reproducer> &InstanceImpl(); 293343181Sdim 294343181Sdim llvm::Optional<Generator> m_generator; 295343181Sdim llvm::Optional<Loader> m_loader; 296343181Sdim 297343181Sdim mutable std::mutex m_mutex; 298343181Sdim}; 299343181Sdim 300343181Sdim} // namespace repro 301343181Sdim} // namespace lldb_private 302343181Sdim 303343181Sdim#endif // LLDB_UTILITY_REPRODUCER_H 304