1343181Sdim//===-- Reproducer.cpp ------------------------------------------*- 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#include "lldb/Utility/Reproducer.h" 10343181Sdim#include "lldb/Utility/LLDBAssert.h" 11343181Sdim 12343181Sdim#include "llvm/Support/FileSystem.h" 13343181Sdim#include "llvm/Support/Threading.h" 14343181Sdim#include "llvm/Support/raw_ostream.h" 15343181Sdim 16343181Sdimusing namespace lldb_private; 17343181Sdimusing namespace lldb_private::repro; 18343181Sdimusing namespace llvm; 19343181Sdimusing namespace llvm::yaml; 20343181Sdim 21343181SdimReproducer &Reproducer::Instance() { return *InstanceImpl(); } 22343181Sdim 23343181Sdimllvm::Error Reproducer::Initialize(ReproducerMode mode, 24343181Sdim llvm::Optional<FileSpec> root) { 25343181Sdim lldbassert(!InstanceImpl() && "Already initialized."); 26343181Sdim InstanceImpl().emplace(); 27343181Sdim 28360784Sdim // The environment can override the capture mode. 29360784Sdim if (mode != ReproducerMode::Replay) { 30360784Sdim std::string env = 31360784Sdim llvm::StringRef(getenv("LLDB_CAPTURE_REPRODUCER")).lower(); 32360784Sdim if (env == "0" || env == "off") 33360784Sdim mode = ReproducerMode::Off; 34360784Sdim else if (env == "1" || env == "on") 35360784Sdim mode = ReproducerMode::Capture; 36360784Sdim } 37360784Sdim 38343181Sdim switch (mode) { 39343181Sdim case ReproducerMode::Capture: { 40343181Sdim if (!root) { 41343181Sdim SmallString<128> repro_dir; 42343181Sdim auto ec = sys::fs::createUniqueDirectory("reproducer", repro_dir); 43343181Sdim if (ec) 44343181Sdim return make_error<StringError>( 45343181Sdim "unable to create unique reproducer directory", ec); 46343181Sdim root.emplace(repro_dir); 47343181Sdim } else { 48343181Sdim auto ec = sys::fs::create_directory(root->GetPath()); 49343181Sdim if (ec) 50343181Sdim return make_error<StringError>("unable to create reproducer directory", 51343181Sdim ec); 52343181Sdim } 53343181Sdim return Instance().SetCapture(root); 54343181Sdim } break; 55343181Sdim case ReproducerMode::Replay: 56343181Sdim return Instance().SetReplay(root); 57343181Sdim case ReproducerMode::Off: 58343181Sdim break; 59343181Sdim }; 60343181Sdim 61343181Sdim return Error::success(); 62343181Sdim} 63343181Sdim 64353358Sdimbool Reproducer::Initialized() { return InstanceImpl().operator bool(); } 65353358Sdim 66343181Sdimvoid Reproducer::Terminate() { 67343181Sdim lldbassert(InstanceImpl() && "Already terminated."); 68343181Sdim InstanceImpl().reset(); 69343181Sdim} 70343181Sdim 71343181SdimOptional<Reproducer> &Reproducer::InstanceImpl() { 72343181Sdim static Optional<Reproducer> g_reproducer; 73343181Sdim return g_reproducer; 74343181Sdim} 75343181Sdim 76343181Sdimconst Generator *Reproducer::GetGenerator() const { 77343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 78343181Sdim if (m_generator) 79343181Sdim return &(*m_generator); 80343181Sdim return nullptr; 81343181Sdim} 82343181Sdim 83343181Sdimconst Loader *Reproducer::GetLoader() const { 84343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 85343181Sdim if (m_loader) 86343181Sdim return &(*m_loader); 87343181Sdim return nullptr; 88343181Sdim} 89343181Sdim 90343181SdimGenerator *Reproducer::GetGenerator() { 91343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 92343181Sdim if (m_generator) 93343181Sdim return &(*m_generator); 94343181Sdim return nullptr; 95343181Sdim} 96343181Sdim 97343181SdimLoader *Reproducer::GetLoader() { 98343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 99343181Sdim if (m_loader) 100343181Sdim return &(*m_loader); 101343181Sdim return nullptr; 102343181Sdim} 103343181Sdim 104343181Sdimllvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { 105343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 106343181Sdim 107343181Sdim if (root && m_loader) 108343181Sdim return make_error<StringError>( 109343181Sdim "cannot generate a reproducer when replay one", 110343181Sdim inconvertibleErrorCode()); 111343181Sdim 112343181Sdim if (!root) { 113343181Sdim m_generator.reset(); 114343181Sdim return Error::success(); 115343181Sdim } 116343181Sdim 117343181Sdim m_generator.emplace(*root); 118343181Sdim return Error::success(); 119343181Sdim} 120343181Sdim 121343181Sdimllvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) { 122343181Sdim std::lock_guard<std::mutex> guard(m_mutex); 123343181Sdim 124343181Sdim if (root && m_generator) 125343181Sdim return make_error<StringError>( 126343181Sdim "cannot replay a reproducer when generating one", 127343181Sdim inconvertibleErrorCode()); 128343181Sdim 129343181Sdim if (!root) { 130343181Sdim m_loader.reset(); 131343181Sdim return Error::success(); 132343181Sdim } 133343181Sdim 134343181Sdim m_loader.emplace(*root); 135343181Sdim if (auto e = m_loader->LoadIndex()) 136343181Sdim return e; 137343181Sdim 138343181Sdim return Error::success(); 139343181Sdim} 140343181Sdim 141343181SdimFileSpec Reproducer::GetReproducerPath() const { 142343181Sdim if (auto g = GetGenerator()) 143343181Sdim return g->GetRoot(); 144343181Sdim if (auto l = GetLoader()) 145343181Sdim return l->GetRoot(); 146343181Sdim return {}; 147343181Sdim} 148343181Sdim 149360784Sdimstatic FileSpec MakeAbsolute(FileSpec file_spec) { 150360784Sdim SmallString<128> path; 151360784Sdim file_spec.GetPath(path, false); 152360784Sdim llvm::sys::fs::make_absolute(path); 153360784Sdim return FileSpec(path, file_spec.GetPathStyle()); 154360784Sdim} 155343181Sdim 156360784SdimGenerator::Generator(FileSpec root) : m_root(MakeAbsolute(std::move(root))) { 157360784Sdim GetOrCreate<repro::WorkingDirectoryProvider>(); 158360784Sdim} 159343181Sdim 160360784SdimGenerator::~Generator() { 161360784Sdim if (!m_done) 162360784Sdim Discard(); 163360784Sdim} 164360784Sdim 165343181SdimProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { 166343181Sdim std::lock_guard<std::mutex> lock(m_providers_mutex); 167343181Sdim std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( 168343181Sdim provider->DynamicClassID(), std::move(provider)); 169343181Sdim auto e = m_providers.insert(std::move(key_value)); 170343181Sdim return e.first->getSecond().get(); 171343181Sdim} 172343181Sdim 173343181Sdimvoid Generator::Keep() { 174343181Sdim assert(!m_done); 175343181Sdim m_done = true; 176343181Sdim 177343181Sdim for (auto &provider : m_providers) 178343181Sdim provider.second->Keep(); 179343181Sdim 180343181Sdim AddProvidersToIndex(); 181343181Sdim} 182343181Sdim 183343181Sdimvoid Generator::Discard() { 184343181Sdim assert(!m_done); 185343181Sdim m_done = true; 186343181Sdim 187343181Sdim for (auto &provider : m_providers) 188343181Sdim provider.second->Discard(); 189343181Sdim 190343181Sdim llvm::sys::fs::remove_directories(m_root.GetPath()); 191343181Sdim} 192343181Sdim 193343181Sdimconst FileSpec &Generator::GetRoot() const { return m_root; } 194343181Sdim 195343181Sdimvoid Generator::AddProvidersToIndex() { 196343181Sdim FileSpec index = m_root; 197343181Sdim index.AppendPathComponent("index.yaml"); 198343181Sdim 199343181Sdim std::error_code EC; 200360784Sdim auto strm = std::make_unique<raw_fd_ostream>(index.GetPath(), EC, 201360784Sdim sys::fs::OpenFlags::OF_None); 202343181Sdim yaml::Output yout(*strm); 203343181Sdim 204353358Sdim std::vector<std::string> files; 205353358Sdim files.reserve(m_providers.size()); 206343181Sdim for (auto &provider : m_providers) { 207353358Sdim files.emplace_back(provider.second->GetFile()); 208343181Sdim } 209353358Sdim 210353358Sdim yout << files; 211343181Sdim} 212343181Sdim 213360784SdimLoader::Loader(FileSpec root) 214360784Sdim : m_root(MakeAbsolute(std::move(root))), m_loaded(false) {} 215343181Sdim 216343181Sdimllvm::Error Loader::LoadIndex() { 217343181Sdim if (m_loaded) 218343181Sdim return llvm::Error::success(); 219343181Sdim 220343181Sdim FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); 221343181Sdim 222343181Sdim auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 223343181Sdim if (auto err = error_or_file.getError()) 224343181Sdim return make_error<StringError>("unable to load reproducer index", err); 225343181Sdim 226343181Sdim yaml::Input yin((*error_or_file)->getBuffer()); 227353358Sdim yin >> m_files; 228343181Sdim if (auto err = yin.error()) 229343181Sdim return make_error<StringError>("unable to read reproducer index", err); 230343181Sdim 231353358Sdim // Sort files to speed up search. 232353358Sdim llvm::sort(m_files); 233343181Sdim 234353358Sdim // Remember that we've loaded the index. 235343181Sdim m_loaded = true; 236343181Sdim 237343181Sdim return llvm::Error::success(); 238343181Sdim} 239343181Sdim 240353358Sdimbool Loader::HasFile(StringRef file) { 241343181Sdim assert(m_loaded); 242353358Sdim auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); 243353358Sdim return (it != m_files.end()) && (*it == file); 244353358Sdim} 245343181Sdim 246353358Sdimllvm::Expected<std::unique_ptr<DataRecorder>> 247353358SdimDataRecorder::Create(const FileSpec &filename) { 248353358Sdim std::error_code ec; 249360784Sdim auto recorder = std::make_unique<DataRecorder>(std::move(filename), ec); 250353358Sdim if (ec) 251353358Sdim return llvm::errorCodeToError(ec); 252353358Sdim return std::move(recorder); 253353358Sdim} 254343181Sdim 255353358SdimDataRecorder *CommandProvider::GetNewDataRecorder() { 256353358Sdim std::size_t i = m_data_recorders.size() + 1; 257353358Sdim std::string filename = (llvm::Twine(Info::name) + llvm::Twine("-") + 258360784Sdim llvm::Twine(i) + llvm::Twine(".yaml")) 259353358Sdim .str(); 260353358Sdim auto recorder_or_error = 261353358Sdim DataRecorder::Create(GetRoot().CopyByAppendingPathComponent(filename)); 262353358Sdim if (!recorder_or_error) { 263353358Sdim llvm::consumeError(recorder_or_error.takeError()); 264353358Sdim return nullptr; 265353358Sdim } 266353358Sdim 267353358Sdim m_data_recorders.push_back(std::move(*recorder_or_error)); 268353358Sdim return m_data_recorders.back().get(); 269343181Sdim} 270343181Sdim 271353358Sdimvoid CommandProvider::Keep() { 272353358Sdim std::vector<std::string> files; 273353358Sdim for (auto &recorder : m_data_recorders) { 274353358Sdim recorder->Stop(); 275353358Sdim files.push_back(recorder->GetFilename().GetPath()); 276353358Sdim } 277353358Sdim 278353358Sdim FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 279353358Sdim std::error_code ec; 280360784Sdim llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 281353358Sdim if (ec) 282353358Sdim return; 283353358Sdim yaml::Output yout(os); 284353358Sdim yout << files; 285353358Sdim} 286353358Sdim 287353358Sdimvoid CommandProvider::Discard() { m_data_recorders.clear(); } 288353358Sdim 289353358Sdimvoid VersionProvider::Keep() { 290353358Sdim FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 291353358Sdim std::error_code ec; 292360784Sdim llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 293353358Sdim if (ec) 294353358Sdim return; 295353358Sdim os << m_version << "\n"; 296353358Sdim} 297353358Sdim 298360784Sdimvoid WorkingDirectoryProvider::Keep() { 299360784Sdim FileSpec file = GetRoot().CopyByAppendingPathComponent(Info::file); 300360784Sdim std::error_code ec; 301360784Sdim llvm::raw_fd_ostream os(file.GetPath(), ec, llvm::sys::fs::OF_Text); 302360784Sdim if (ec) 303360784Sdim return; 304360784Sdim os << m_cwd << "\n"; 305360784Sdim} 306360784Sdim 307343181Sdimvoid ProviderBase::anchor() {} 308353358Sdimchar CommandProvider::ID = 0; 309353358Sdimchar FileProvider::ID = 0; 310360784Sdimchar ProviderBase::ID = 0; 311353358Sdimchar VersionProvider::ID = 0; 312360784Sdimchar WorkingDirectoryProvider::ID = 0; 313353358Sdimconst char *CommandProvider::Info::file = "command-interpreter.yaml"; 314353358Sdimconst char *CommandProvider::Info::name = "command-interpreter"; 315353358Sdimconst char *FileProvider::Info::file = "files.yaml"; 316353358Sdimconst char *FileProvider::Info::name = "files"; 317353358Sdimconst char *VersionProvider::Info::file = "version.txt"; 318353358Sdimconst char *VersionProvider::Info::name = "version"; 319360784Sdimconst char *WorkingDirectoryProvider::Info::file = "cwd.txt"; 320360784Sdimconst char *WorkingDirectoryProvider::Info::name = "cwd"; 321