1 //===-- Reproducer.cpp ------------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Utility/Reproducer.h" 11 #include "lldb/Host/HostInfo.h" 12 13 #include "llvm/Support/FileSystem.h" 14 #include "llvm/Support/Threading.h" 15 #include "llvm/Support/raw_ostream.h" 16 17 using namespace lldb_private; 18 using namespace lldb_private::repro; 19 using namespace llvm; 20 using namespace llvm::yaml; 21 22 Reproducer &Reproducer::Instance() { 23 static Reproducer g_reproducer; 24 return g_reproducer; 25 } 26 27 const Generator *Reproducer::GetGenerator() const { 28 std::lock_guard<std::mutex> guard(m_mutex); 29 if (m_generate_reproducer) 30 return &m_generator; 31 return nullptr; 32 } 33 34 const Loader *Reproducer::GetLoader() const { 35 std::lock_guard<std::mutex> guard(m_mutex); 36 if (m_replay_reproducer) 37 return &m_loader; 38 return nullptr; 39 } 40 41 Generator *Reproducer::GetGenerator() { 42 std::lock_guard<std::mutex> guard(m_mutex); 43 if (m_generate_reproducer) 44 return &m_generator; 45 return nullptr; 46 } 47 48 Loader *Reproducer::GetLoader() { 49 std::lock_guard<std::mutex> guard(m_mutex); 50 if (m_replay_reproducer) 51 return &m_loader; 52 return nullptr; 53 } 54 55 llvm::Error Reproducer::SetGenerateReproducer(bool value) { 56 std::lock_guard<std::mutex> guard(m_mutex); 57 58 if (value && m_replay_reproducer) 59 return make_error<StringError>( 60 "cannot generate a reproducer when replay one", 61 inconvertibleErrorCode()); 62 63 m_generate_reproducer = value; 64 m_generator.SetEnabled(value); 65 66 return Error::success(); 67 } 68 69 llvm::Error Reproducer::SetReplayReproducer(bool value) { 70 std::lock_guard<std::mutex> guard(m_mutex); 71 72 if (value && m_generate_reproducer) 73 return make_error<StringError>( 74 "cannot replay a reproducer when generating one", 75 inconvertibleErrorCode()); 76 77 m_replay_reproducer = value; 78 79 return Error::success(); 80 } 81 82 llvm::Error Reproducer::SetReproducerPath(const FileSpec &path) { 83 // Setting the path implies using the reproducer. 84 if (auto e = SetReplayReproducer(true)) 85 return e; 86 87 // Tell the reproducer to load the index form the given path. 88 if (auto loader = GetLoader()) { 89 if (auto e = loader->LoadIndex(path)) 90 return e; 91 } 92 93 return make_error<StringError>("unable to get loader", 94 inconvertibleErrorCode()); 95 } 96 97 FileSpec Reproducer::GetReproducerPath() const { 98 if (auto g = GetGenerator()) 99 return g->GetDirectory(); 100 return {}; 101 } 102 103 Generator::Generator() : m_enabled(false), m_done(false) { 104 m_directory = HostInfo::GetReproducerTempDir(); 105 } 106 107 Generator::~Generator() {} 108 109 Provider &Generator::Register(std::unique_ptr<Provider> provider) { 110 std::lock_guard<std::mutex> lock(m_providers_mutex); 111 112 AddProviderToIndex(provider->GetInfo()); 113 114 m_providers.push_back(std::move(provider)); 115 return *m_providers.back(); 116 } 117 118 void Generator::Keep() { 119 assert(!m_done); 120 m_done = true; 121 122 if (!m_enabled) 123 return; 124 125 for (auto &provider : m_providers) 126 provider->Keep(); 127 } 128 129 void Generator::Discard() { 130 assert(!m_done); 131 m_done = true; 132 133 if (!m_enabled) 134 return; 135 136 for (auto &provider : m_providers) 137 provider->Discard(); 138 139 llvm::sys::fs::remove_directories(m_directory.GetPath()); 140 } 141 142 void Generator::ChangeDirectory(const FileSpec &directory) { 143 assert(m_providers.empty() && "Changing the directory after providers have " 144 "been registered would invalidate the index."); 145 m_directory = directory; 146 } 147 148 const FileSpec &Generator::GetDirectory() const { return m_directory; } 149 150 void Generator::AddProviderToIndex(const ProviderInfo &provider_info) { 151 FileSpec index = m_directory; 152 index.AppendPathComponent("index.yaml"); 153 154 std::error_code EC; 155 auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC, 156 sys::fs::OpenFlags::F_None); 157 yaml::Output yout(*strm); 158 yout << const_cast<ProviderInfo &>(provider_info); 159 } 160 161 Loader::Loader() : m_loaded(false) {} 162 163 llvm::Error Loader::LoadIndex(const FileSpec &directory) { 164 if (m_loaded) 165 return llvm::Error::success(); 166 167 FileSpec index = directory.CopyByAppendingPathComponent("index.yaml"); 168 169 auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 170 if (auto err = error_or_file.getError()) 171 return errorCodeToError(err); 172 173 std::vector<ProviderInfo> provider_info; 174 yaml::Input yin((*error_or_file)->getBuffer()); 175 yin >> provider_info; 176 177 if (auto err = yin.error()) 178 return errorCodeToError(err); 179 180 for (auto &info : provider_info) 181 m_provider_info[info.name] = info; 182 183 m_directory = directory; 184 m_loaded = true; 185 186 return llvm::Error::success(); 187 } 188 189 llvm::Optional<ProviderInfo> Loader::GetProviderInfo(StringRef name) { 190 assert(m_loaded); 191 192 auto it = m_provider_info.find(name); 193 if (it == m_provider_info.end()) 194 return llvm::None; 195 196 return it->second; 197 } 198