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 // The environment can override the capture mode. 29 if (mode != ReproducerMode::Replay) { 30 std::string env = 31 llvm::StringRef(getenv("LLDB_CAPTURE_REPRODUCER")).lower(); 32 if (env == "0" || env == "off") 33 mode = ReproducerMode::Off; 34 else if (env == "1" || env == "on") 35 mode = ReproducerMode::Capture; 36 } 37 38 switch (mode) { 39 case ReproducerMode::Capture: { 40 if (!root) { 41 SmallString<128> repro_dir; 42 auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir); 43 if (ec) 44 return make_error<StringError>( 45 "unable to create unique reproducer directory", ec); 46 root.emplace(repro_dir); 47 } else { 48 auto ec = sys::fs::create_directory(root->GetPath()); 49 if (ec) 50 return make_error<StringError>("unable to create reproducer directory", 51 ec); 52 } 53 return Instance().SetCapture(root); 54 } break; 55 case ReproducerMode::Replay: 56 return Instance().SetReplay(root); 57 case ReproducerMode::Off: 58 break; 59 }; 60 61 return Error::success(); 62 } 63 64 bool Reproducer::Initialized() { return InstanceImpl().operator bool(); } 65 66 void Reproducer::Terminate() { 67 lldbassert(InstanceImpl() && "Already terminated."); 68 InstanceImpl().reset(); 69 } 70 71 Optional<Reproducer> &Reproducer::InstanceImpl() { 72 static Optional<Reproducer> g_reproducer; 73 return g_reproducer; 74 } 75 76 const Generator *Reproducer::GetGenerator() const { 77 std::lock_guard<std::mutex> guard(m_mutex); 78 if (m_generator) 79 return &(*m_generator); 80 return nullptr; 81 } 82 83 const Loader *Reproducer::GetLoader() const { 84 std::lock_guard<std::mutex> guard(m_mutex); 85 if (m_loader) 86 return &(*m_loader); 87 return nullptr; 88 } 89 90 Generator *Reproducer::GetGenerator() { 91 std::lock_guard<std::mutex> guard(m_mutex); 92 if (m_generator) 93 return &(*m_generator); 94 return nullptr; 95 } 96 97 Loader *Reproducer::GetLoader() { 98 std::lock_guard<std::mutex> guard(m_mutex); 99 if (m_loader) 100 return &(*m_loader); 101 return nullptr; 102 } 103 104 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { 105 std::lock_guard<std::mutex> guard(m_mutex); 106 107 if (root && m_loader) 108 return make_error<StringError>( 109 "cannot generate a reproducer when replay one", 110 inconvertibleErrorCode()); 111 112 if (!root) { 113 m_generator.reset(); 114 return Error::success(); 115 } 116 117 m_generator.emplace(*root); 118 return Error::success(); 119 } 120 121 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) { 122 std::lock_guard<std::mutex> guard(m_mutex); 123 124 if (root && m_generator) 125 return make_error<StringError>( 126 "cannot replay a reproducer when generating one", 127 inconvertibleErrorCode()); 128 129 if (!root) { 130 m_loader.reset(); 131 return Error::success(); 132 } 133 134 m_loader.emplace(*root); 135 if (auto e = m_loader->LoadIndex()) 136 return e; 137 138 return Error::success(); 139 } 140 141 FileSpec Reproducer::GetReproducerPath() const { 142 if (auto g = GetGenerator()) 143 return g->GetRoot(); 144 if (auto l = GetLoader()) 145 return l->GetRoot(); 146 return {}; 147 } 148 149 static FileSpec MakeAbsolute(FileSpec file_spec) { 150 SmallString<128> path; 151 file_spec.GetPath(path, false); 152 llvm::sys::fs::make_absolute(path); 153 return FileSpec(path, file_spec.GetPathStyle()); 154 } 155 156 Generator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) { 157 GetOrCreate<repro::WorkingDirectoryProvider>(); 158 } 159 160 Generator::~Generator() { 161 if (!m_done) 162 Discard(); 163 } 164 165 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { 166 std::lock_guard<std::mutex> lock(m_providers_mutex); 167 std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( 168 provider->DynamicClassID(), std::move(provider)); 169 auto e = m_providers.insert(std::move(key_value)); 170 return e.first->getSecond().get(); 171 } 172 173 void Generator::Keep() { 174 assert(!m_done); 175 m_done = true; 176 177 for (auto &provider : m_providers) 178 provider.second->Keep(); 179 180 AddProvidersToIndex(); 181 } 182 183 void Generator::Discard() { 184 assert(!m_done); 185 m_done = true; 186 187 for (auto &provider : m_providers) 188 provider.second->Discard(); 189 190 llvm::sys::fs::remove_directories(m_root.GetPath()); 191 } 192 193 const FileSpec &Generator::GetRoot() const { return m_root; } 194 195 void Generator::AddProvidersToIndex() { 196 FileSpec index = m_root; 197 index.AppendPathComponent("index.yaml"); 198 199 std::error_code EC; 200 auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC, 201 sys::fs::OpenFlags::OF_None); 202 yaml::Output yout(*strm); 203 204 std::vector<std::string> files; 205 files.reserve(m_providers.size()); 206 for (auto &provider : m_providers) { 207 files.emplace_back(provider.second->GetFile()); 208 } 209 210 yout << files; 211 } 212 213 Loader::Loader(FileSpec root) 214 : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {} 215 216 llvm::Error Loader::LoadIndex() { 217 if (m_loaded) 218 return llvm::Error::success(); 219 220 FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); 221 222 auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 223 if (auto err = error_or_file.getError()) 224 return make_error<StringError>("unable to load reproducer index", err); 225 226 yaml::Input yin((*error_or_file)->getBuffer()); 227 yin >> m_files; 228 if (auto err = yin.error()) 229 return make_error<StringError>("unable to read reproducer index", err); 230 231 // Sort files to speed up search. 232 llvm::sort(m_files); 233 234 // Remember that we've loaded the index. 235 m_loaded = true; 236 237 return llvm::Error::success(); 238 } 239 240 bool Loader::HasFile(StringRef file) { 241 assert(m_loaded); 242 auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); 243 return (it != m_files.end()) && (*it == file); 244 } 245 246 llvm::Expected<std::unique_ptr<DataRecorder>> 247 DataRecorder::Create(const FileSpec &filename) { 248 std::error_code ec; 249 auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec); 250 if (ec) 251 return llvm::errorCodeToError(ec); 252 return std::move(recorder); 253 } 254 255 DataRecorder *CommandProvider::GetNewDataRecorder() { 256 std::size_t i = m_data_recorders.size() + 1; 257 std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + 258 llvm::Twine(i) + llvm::Twine(".yaml")) 259 .str(); 260 auto recorder_or_error = 261 DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); 262 if (!recorder_or_error) { 263 llvm::consumeError(recorder_or_error.takeError()); 264 return nullptr; 265 } 266 267 m_data_recorders.push_back(std::move(*recorder_or_error)); 268 return m_data_recorders.back().get(); 269 } 270 271 void CommandProvider::Keep() { 272 std::vector<std::string> files; 273 for (auto &recorder : m_data_recorders) { 274 recorder->Stop(); 275 files.push_back(recorder->GetFilename().GetPath()); 276 } 277 278 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 279 std::error_code ec; 280 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 281 if (ec) 282 return; 283 yaml::Output yout(os); 284 yout << files; 285 } 286 287 void CommandProvider::Discard() { m_data_recorders.clear(); } 288 289 void VersionProvider::Keep() { 290 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 291 std::error_code ec; 292 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 293 if (ec) 294 return; 295 os << m_version << "\n"; 296 } 297 298 void WorkingDirectoryProvider::Keep() { 299 FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 300 std::error_code ec; 301 llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 302 if (ec) 303 return; 304 os << m_cwd << "\n"; 305 } 306 307 void ProviderBase::anchor() {} 308 char CommandProvider::ID = 0; 309 char FileProvider::ID = 0; 310 char ProviderBase::ID = 0; 311 char VersionProvider::ID = 0; 312 char WorkingDirectoryProvider::ID = 0; 313 const char *CommandProvider::Info::file = "command-interpreter.yaml"; 314 const char *CommandProvider::Info::name = "command-interpreter"; 315 const char *FileProvider::Info::file = "files.yaml"; 316 const char *FileProvider::Info::name = "files"; 317 const char *VersionProvider::Info::file = "version.txt"; 318 const char *VersionProvider::Info::name = "version"; 319 const char *WorkingDirectoryProvider::Info::file = "cwd.txt"; 320 const char *WorkingDirectoryProvider::Info::name = "cwd"; 321