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 void Reproducer::Terminate() { 55 lldbassert(InstanceImpl() && "Already terminated."); 56 InstanceImpl().reset(); 57 } 58 59 Optional<Reproducer> &Reproducer::InstanceImpl() { 60 static Optional<Reproducer> g_reproducer; 61 return g_reproducer; 62 } 63 64 const Generator *Reproducer::GetGenerator() const { 65 std::lock_guard<std::mutex> guard(m_mutex); 66 if (m_generator) 67 return &(*m_generator); 68 return nullptr; 69 } 70 71 const Loader *Reproducer::GetLoader() const { 72 std::lock_guard<std::mutex> guard(m_mutex); 73 if (m_loader) 74 return &(*m_loader); 75 return nullptr; 76 } 77 78 Generator *Reproducer::GetGenerator() { 79 std::lock_guard<std::mutex> guard(m_mutex); 80 if (m_generator) 81 return &(*m_generator); 82 return nullptr; 83 } 84 85 Loader *Reproducer::GetLoader() { 86 std::lock_guard<std::mutex> guard(m_mutex); 87 if (m_loader) 88 return &(*m_loader); 89 return nullptr; 90 } 91 92 llvm::Error Reproducer::SetCapture(llvm::Optional<FileSpec> root) { 93 std::lock_guard<std::mutex> guard(m_mutex); 94 95 if (root && m_loader) 96 return make_error<StringError>( 97 "cannot generate a reproducer when replay one", 98 inconvertibleErrorCode()); 99 100 if (!root) { 101 m_generator.reset(); 102 return Error::success(); 103 } 104 105 m_generator.emplace(*root); 106 return Error::success(); 107 } 108 109 llvm::Error Reproducer::SetReplay(llvm::Optional<FileSpec> root) { 110 std::lock_guard<std::mutex> guard(m_mutex); 111 112 if (root && m_generator) 113 return make_error<StringError>( 114 "cannot replay a reproducer when generating one", 115 inconvertibleErrorCode()); 116 117 if (!root) { 118 m_loader.reset(); 119 return Error::success(); 120 } 121 122 m_loader.emplace(*root); 123 if (auto e = m_loader->LoadIndex()) 124 return e; 125 126 return Error::success(); 127 } 128 129 FileSpec Reproducer::GetReproducerPath() const { 130 if (auto g = GetGenerator()) 131 return g->GetRoot(); 132 if (auto l = GetLoader()) 133 return l->GetRoot(); 134 return {}; 135 } 136 137 Generator::Generator(const FileSpec &root) : m_root(root), m_done(false) {} 138 139 Generator::~Generator() {} 140 141 ProviderBase *Generator::Register(std::unique_ptr<ProviderBase> provider) { 142 std::lock_guard<std::mutex> lock(m_providers_mutex); 143 std::pair<const void *, std::unique_ptr<ProviderBase>> key_value( 144 provider->DynamicClassID(), std::move(provider)); 145 auto e = m_providers.insert(std::move(key_value)); 146 return e.first->getSecond().get(); 147 } 148 149 void Generator::Keep() { 150 assert(!m_done); 151 m_done = true; 152 153 for (auto &provider : m_providers) 154 provider.second->Keep(); 155 156 AddProvidersToIndex(); 157 } 158 159 void Generator::Discard() { 160 assert(!m_done); 161 m_done = true; 162 163 for (auto &provider : m_providers) 164 provider.second->Discard(); 165 166 llvm::sys::fs::remove_directories(m_root.GetPath()); 167 } 168 169 const FileSpec &Generator::GetRoot() const { return m_root; } 170 171 void Generator::AddProvidersToIndex() { 172 FileSpec index = m_root; 173 index.AppendPathComponent("index.yaml"); 174 175 std::error_code EC; 176 auto strm = llvm::make_unique<raw_fd_ostream>(index.GetPath(), EC, 177 sys::fs::OpenFlags::F_None); 178 yaml::Output yout(*strm); 179 180 std::vector<std::string> files; 181 files.reserve(m_providers.size()); 182 for (auto &provider : m_providers) { 183 files.emplace_back(provider.second->GetFile()); 184 } 185 186 yout << files; 187 } 188 189 Loader::Loader(const FileSpec &root) : m_root(root), m_loaded(false) {} 190 191 llvm::Error Loader::LoadIndex() { 192 if (m_loaded) 193 return llvm::Error::success(); 194 195 FileSpec index = m_root.CopyByAppendingPathComponent("index.yaml"); 196 197 auto error_or_file = MemoryBuffer::getFile(index.GetPath()); 198 if (auto err = error_or_file.getError()) 199 return make_error<StringError>("unable to load reproducer index", err); 200 201 yaml::Input yin((*error_or_file)->getBuffer()); 202 yin >> m_files; 203 if (auto err = yin.error()) 204 return make_error<StringError>("unable to read reproducer index", err); 205 206 // Sort files to speed up search. 207 llvm::sort(m_files); 208 209 // Remember that we've loaded the index. 210 m_loaded = true; 211 212 return llvm::Error::success(); 213 } 214 215 bool Loader::HasFile(StringRef file) { 216 assert(m_loaded); 217 auto it = std::lower_bound(m_files.begin(), m_files.end(), file.str()); 218 return (it != m_files.end()) && (*it == file); 219 } 220 221 void ProviderBase::anchor() {} 222 char ProviderBase::ID = 0; 223