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