1 //===-- Reproducer.cpp ------------------------------------------*- 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 #include "lldb/Utility/Reproducer.h" 10 #include "lldb/Utility/LLDBAssert.h" 11 12 #include "llvm/Support/FileSystem.h" 13 #include "llvm/Support/Threading.h" 14 #include "llvm/Support/raw_ostream.h" 15 16 using namespace lldb_private; 17 using namespace lldb_private::repro; 18 using namespace llvm; 19 using namespace llvm::yaml; 20 21 Reproducer &Reproducer::Instance() { return *InstanceImpl(); } 22 23 llvm::Error Reproducer::Initialize(ReproducerMode mode, 24 llvm::Optional<FileSpec> root) { 25 lldbassert(!InstanceImpl() && "Already initialized."); 26 InstanceImpl().emplace(); 27 28 switch (mode) { 29 case ReproducerMode::Capture: { 30 if (!root) { 31 SmallString<128> repro_dir; 32 auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir); 33 if (ec) 34 return make_error<StringError>( 35 "unable to create unique reproducer directory", ec); 36 root.emplace(repro_dir); 37 } else { 38 auto ec = sys::fs::create_directory(root->GetPath()); 39 if (ec) 40 return make_error<StringError>("unable to create reproducer directory", 41 ec); 42 } 43 return Instance().SetCapture(root); 44 } break; 45 case ReproducerMode::Replay: 46 return Instance().SetReplay(root); 47 case ReproducerMode::Off: 48 break; 49 }; 50 51 return Error::success(); 52 } 53 54 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); } 55 56 void Reproducer::Terminate() { 57 lldbassert(InstanceImpl() && "Already terminated."); 58 InstanceImpl().reset(); 59 } 60 61 Optional<Reproducer> &Reproducer::InstanceImpl() { 62 static Optional<Reproducer> g_reproducer; 63 return g_reproducer; 64 } 65 66 const Generator *Reproducer::GetGenerator() const { 67 std::lock_guard<std::mutex> guard(m_mutex); 68 if (m_generator) 69 return &(*m_generator); 70 return nullptr; 71 } 72 73 const Loader *Reproducer::GetLoader() const { 74 std::lock_guard<std::mutex> guard(m_mutex); 75 if (m_loader) 76 return &(*m_loader); 77 return nullptr; 78 } 79 80 Generator *Reproducer::GetGenerator() { 81 std::lock_guard<std::mutex> guard(m_mutex); 82 if (m_generator) 83 return &(*m_generator); 84 return nullptr; 85 } 86 87 Loader *Reproducer::GetLoader() { 88 std::lock_guard<std::mutex> guard(m_mutex); 89 if (m_loader) 90 return &(*m_loader); 91 return nullptr; 92 } 93 94 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { 95 std::lock_guard<std::mutex> guard(m_mutex); 96 97 if (root && m_loader) 98 return make_error<StringError>( 99 "cannot generate a reproducer when replay one", 100 inconvertibleErrorCode()); 101 102 if (!root) { 103 m_generator.reset(); 104 return Error::success(); 105 } 106 107 m_generator.emplace(*root); 108 return Error::success(); 109 } 110 111 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) { 112 std::lock_guard<std::mutex> guard(m_mutex); 113 114 if (root && m_generator) 115 return make_error<StringError>( 116 "cannot replay a reproducer when generating one", 117 inconvertibleErrorCode()); 118 119 if (!root) { 120 m_loader.reset(); 121 return Error::success(); 122 } 123 124 m_loader.emplace(*root); 125 if (auto e = m_loader->LoadIndex()) 126 return e; 127 128 return Error::success(); 129 } 130 131 FileSpec Reproducer::GetReproducerPath() const { 132 if (auto g = GetGenerator()) 133 return g->GetRoot(); 134 if (auto l = GetLoader()) 135 return l->GetRoot(); 136 return {}; 137 } 138 139 Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {} 140 141 Generator::~Generator() {} 142 143 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { 144 std::lock_guard<std::mutex> lock(m_providers_mutex); 145 std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( 146 provider->DynamicClassID(), std::move(provider)); 147 auto e = m_providers.insert(std::move(key_value)); 148 return e.first->getSecond().get(); 149 } 150 151 void Generator::Keep() { 152 assert(!m_done); 153 m_done = true; 154 155 for (auto &provider : m_providers) 156 provider.second->Keep(); 157 158 AddProvidersToIndex(); 159 } 160 161 void Generator::Discard() { 162 assert(!m_done); 163 m_done = true; 164 165 for (auto &provider : m_providers) 166 provider.second->Discard(); 167 168 llvm::sys::fs::remove_directories(m_root.GetPath()); 169 } 170 171 const FileSpec &Generator::GetRoot() const { return m_root; } 172 173 void Generator::AddProvidersToIndex() { 174 FileSpec index = m_root; 175 index.AppendPathComponent("index.yaml"); 176 177 std::error_code EC; 178 auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC, 179 sys::fs::OpenFlags::F_None); 180 yaml::Output yout(*strm); 181 182 std::vector<std::string> files; 183 files.reserve(m_providers.size()); 184 for (auto &provider : m_providers) { 185 files.emplace_back(provider.second->GetFile()); 186 } 187 188 yout << files; 189 } 190 191 Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {} 192 193 llvm::Error Loader::LoadIndex() { 194 if (m_loaded) 195 return llvm::Error::success(); 196 197 FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); 198 199 auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 200 if (auto err = error_or_file.getError()) 201 return make_error<StringError>("unable to load reproducer index", err); 202 203 yaml::Input yin((*error_or_file)->getBuffer()); 204 yin >> m_files; 205 if (auto err = yin.error()) 206 return make_error<StringError>("unable to read reproducer index", err); 207 208 // Sort files to speed up search. 209 llvm::sort(m_files); 210 211 // Remember that we've loaded the index. 212 m_loaded = true; 213 214 return llvm::Error::success(); 215 } 216 217 bool Loader::HasFile(StringRef file) { 218 assert(m_loaded); 219 auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); 220 return (it != m_files.end()) && (*it == file); 221 } 222 223 llvm::Expected<std::unique_ptr<DataRecorder>> 224 DataRecorder::Create(FileSpec filename) { 225 std::error_code ec; 226 auto recorder = llvm::make_unique<DataRecorder>(std::move(filename), ec); 227 if (ec) 228 return llvm::errorCodeToError(ec); 229 return std::move(recorder); 230 } 231 232 DataRecorder *CommandProvider::GetNewDataRecorder() { 233 std::size_t i = m_data_recorders.size() + 1; 234 std::string filename = (llvm::Twine(info::name) + llvm::Twine("-") + 235 llvm::Twine(i) + llvm::Twine(".txt")) 236 .str(); 237 auto recorder_or_error = 238 DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); 239 if (!recorder_or_error) { 240 llvm::consumeError(recorder_or_error.takeError()); 241 return nullptr; 242 } 243 244 m_data_recorders.push_back(std::move(*recorder_or_error)); 245 return m_data_recorders.back().get(); 246 } 247 248 void CommandProvider::Keep() { 249 std::vector<std::string> files; 250 for (auto &recorder : m_data_recorders) 251 files.push_back(recorder->GetFilename().GetPath()); 252 253 FileSpec file = GetRoot().CopyByAppendingPathComponent(info::file); 254 std::error_code ec; 255 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::F_Text); 256 if (ec) 257 return; 258 yaml::Output yout(os); 259 yout << files; 260 261 m_data_recorders.clear(); 262 } 263 264 void CommandProvider::Discard() { m_data_recorders.clear(); } 265 266 void ProviderBase::anchor() {} 267 char ProviderBase::ID = 0; 268 char FileProvider::ID = 0; 269 char CommandProvider::ID = 0; 270 const char *FileInfo::name = "files"; 271 const char *FileInfo::file = "files.yaml"; 272 const char *CommandInfo::name = "command-interpreter"; 273 const char *CommandInfo::file = "command-interpreter.yaml"; 274