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